From af2accd8b1d1f07cb8e1fbac2c8086f935542f92 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Fri, 2 Apr 2021 19:12:02 -0400 Subject: [PATCH 001/280] Inrement version to 5.3.0.4. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a2f0380a3a..97bc47b603 100755 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 3) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 3) +define(_CLIENT_VERSION_BUILD, 4) define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS,[The %s developers]) From 54595e5a63724fd4384cd2804e85dd23e38a3d83 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Fri, 2 Apr 2021 17:45:42 -0700 Subject: [PATCH 002/280] Changes for snapshotdownload sections plus more --- src/gridcoin/gridcoin.cpp | 4 ++-- src/gridcoin/upgrade.cpp | 41 +++++++++++++++++++++++++++--------- src/gridcoin/upgrade.h | 8 ++++++- src/qt/diagnosticsdialog.cpp | 2 +- src/qt/upgradeqt.cpp | 17 +++++++++++++++ 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index 83a94e0892..09d782b9d0 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -356,12 +356,12 @@ void ScheduleUpdateChecks(CScheduler& scheduler) LogPrintf("Gridcoin: checking for updates every %" PRId64 " hours", hours); scheduler.scheduleEvery([]{ - g_UpdateChecker->CheckForLatestUpdate(); + g_UpdateChecker->ScheduledUpdateCheck(); }, hours * 60 * 60 * 1000); // Schedule a start-up check one minute from now: scheduler.scheduleFromNow([]{ - g_UpdateChecker->CheckForLatestUpdate(); + g_UpdateChecker->ScheduledUpdateCheck(); }, 60 * 1000); } diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index faa5172461..263e127bb0 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -38,7 +38,14 @@ Upgrade::Upgrade() ExtractStatus.SnapshotExtractProgress = 0; } -bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_out) +void Upgrade::ScheduledUpdateCheck() +{ + std::string VersionResponse = ""; + + CheckForLatestUpdate(VersionResponse); +} + +bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dialog, bool snapshotrequest) { // If testnet skip this || If the user changes this to disable while wallet running just drop out of here now. (need a way to remove items from scheduler) if (fTestNet || GetBoolArg("-disableupdatecheck", false)) @@ -48,6 +55,7 @@ bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_ou std::string GithubResponse = ""; std::string VersionResponse = ""; + std::string UpdateCheckType = snapshotrequest ? "Snapshot Request" : "Update Checker"; // We receive the response and it's in a json reply UniValue Response(UniValue::VOBJ); @@ -59,14 +67,14 @@ bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_ou catch (const std::runtime_error& e) { - LogPrintf("Update Checker: Exception occurred while checking for latest update. (%s)", e.what()); + LogPrintf("%s: Exception occurred while checking for latest update. (%s)", UpdateCheckType, e.what()); return false; } if (VersionResponse.empty()) { - LogPrintf("Update Checker: No Response from github"); + LogPrintf("%s: No Response from github", UpdateCheckType); return false; } @@ -91,7 +99,7 @@ bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_ou catch (std::exception& ex) { - LogPrintf("Update Checker: Exception occurred while parsing json response (%s)", ex.what()); + LogPrintf("%s: Exception occurred while parsing json response (%s)", UpdateCheckType, ex.what()); return false; } @@ -118,7 +126,7 @@ bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_ou if (GithubVersion.size() != 4) { - LogPrintf("Update Check: Got malformed version (%s)", GithubReleaseData); + LogPrintf("%s: Got malformed version (%s)", UpdateCheckType, GithubReleaseData); return false; } @@ -144,7 +152,7 @@ bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_ou } catch (std::exception& ex) { - LogPrintf("Update Check: Exception occurred checking client version against github version (%s)", ToString(ex.what())); + LogPrintf("%s: Exception occurred checking client version against github version (%s)", UpdateCheckType, ToString(ex.what())); return false; } @@ -156,17 +164,17 @@ bool Upgrade::CheckForLatestUpdate(bool ui_dialog, std::string client_message_ou client_message_out.append(_("Github version: ") + GithubReleaseData + "\r\n"); client_message_out.append(_("This update is ") + GithubReleaseType + "\r\n\r\n"); + // For snapshot requests we will handle things differently after this point + if (snapshotrequest && NewMandatory) + return NewVersion; + if (NewMandatory) - { client_message_out.append(_("WARNING: A mandatory release is available. Please upgrade as soon as possible.") + "\n"); - } std::string ChangeLog = GithubReleaseBody; if (ui_dialog) - { uiInterface.UpdateMessageBox(client_message_out, ChangeLog); - } return NewVersion; } @@ -177,6 +185,19 @@ void Upgrade::SnapshotMain() std::cout << _("Snapshot Process Has Begun.") << std::endl; std::cout << _("Warning: Ending this process after Stage 2 will result in syncing from 0 or an incomplete/corrupted blockchain.") << std::endl << std::endl; + // Verify a mandatory release is not available before we continue to snapshot download. + std::string VersionResponse = ""; + + if (CheckForLatestUpdate(VersionResponse, false, true)) + { + std::cout << std::endl; + std::cout << _("Unable to perform a Snapshot download as the wallet has detected that a new mandatory version is available for download.") << std::endl; + std::cout << _("Latest Version github data response:") << std::endl; + std::cout << VersionResponse << std::endl; + + throw std::runtime_error("Failed to download snapshot as mandatory client is available for download."); + } + // Create a thread for snapshot to be downloaded boost::thread SnapshotDownloadThread(std::bind(&Upgrade::DownloadSnapshot, this)); diff --git a/src/gridcoin/upgrade.h b/src/gridcoin/upgrade.h index 7e2bd07c96..65cf838560 100644 --- a/src/gridcoin/upgrade.h +++ b/src/gridcoin/upgrade.h @@ -42,10 +42,16 @@ class Upgrade //! Upgrade(); + //! + //! \brief Scheduler call to CheckForLatestUpdate + //! + + static void ScheduledUpdateCheck(); + //! //! \brief Check for latest updates on github. //! - static bool CheckForLatestUpdate(bool ui_dialog = true, std::string client_message_out = ""); + static bool CheckForLatestUpdate(std::string& client_message_out, bool ui_dialog = true, bool snapshotrequest = false); //! //! \brief Function that will be threaded to download snapshot diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index 340930377b..fe0c7d2dca 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -457,7 +457,7 @@ void DiagnosticsDialog::on_testButton_clicked() std::string client_message; - if (g_UpdateChecker->CheckForLatestUpdate(false, client_message)) + if (g_UpdateChecker->CheckForLatestUpdate(client_message, false)) { UpdateTestStatus("checkClientVersion", ui->checkClientVersionResultLabel, completed, warning, tr("Warning: New Client version available:\n %1").arg(QString(client_message.c_str()))); diff --git a/src/qt/upgradeqt.cpp b/src/qt/upgradeqt.cpp index 0060f64df6..89618790bb 100644 --- a/src/qt/upgradeqt.cpp +++ b/src/qt/upgradeqt.cpp @@ -29,6 +29,23 @@ bool UpgradeQt::SnapshotMain(QApplication& SnapshotApp) Upgrade UpgradeMain; + // Verify a mandatory release is not available before we continue to snapshot download. + std::string VersionResponse = ""; + + if (UpgradeMain.CheckForLatestUpdate(VersionResponse, false, true)) + { + QMessageBox Msg; + + Msg.setIcon(QMessageBox::Critical); + Msg.setText(ToQString(_("Unable to perform a Snapshot download as the wallet has detected that a new mandatory version is available for download."))); + Msg.setInformativeText(ToQString(_("Latest Version github data response:\r\n") + VersionResponse)); + Msg.setStandardButtons(QMessageBox::Ok); + + Msg.exec(); + + return false; + } + QProgressDialog Progress("", ToQString(_("Cancel")), 0, 100); Progress.setWindowModality(Qt::WindowModal); From a692352aecee0bf3d93fbd7fbe6cda30ee94e602 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Fri, 2 Apr 2021 19:27:08 -0700 Subject: [PATCH 003/280] some clean up and add sync from zero feature --- src/gridcoin/gridcoin.cpp | 1 + src/gridcoin/upgrade.cpp | 10 ++++++--- src/gridcoin/upgrade.h | 10 +++++++-- src/gridcoinresearchd.cpp | 35 +++++++++++++++++++++++++++++ src/init.cpp | 3 ++- src/init.h | 1 + src/qt/bitcoin.cpp | 46 +++++++++++++++++++++++++++++++++++++++ src/qt/bitcoingui.cpp | 42 +++++++++++++++++++++++++++++++++++ src/qt/bitcoingui.h | 2 ++ src/qt/upgradeqt.cpp | 14 ++++++++++++ src/qt/upgradeqt.h | 6 +++++ 11 files changed, 164 insertions(+), 6 deletions(-) diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index 09d782b9d0..05e49f3b94 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -379,6 +379,7 @@ void ScheduleBeaconDBPassivation(CScheduler& scheduler) std::unique_ptr g_UpdateChecker; bool fSnapshotRequest = false; +bool fSyncfromzeroRequest = false; // ----------------------------------------------------------------------------- // Functions diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index 263e127bb0..405760ed34 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -190,7 +190,6 @@ void Upgrade::SnapshotMain() if (CheckForLatestUpdate(VersionResponse, false, true)) { - std::cout << std::endl; std::cout << _("Unable to perform a Snapshot download as the wallet has detected that a new mandatory version is available for download.") << std::endl; std::cout << _("Latest Version github data response:") << std::endl; std::cout << VersionResponse << std::endl; @@ -366,7 +365,7 @@ bool Upgrade::VerifySHA256SUM() } } -bool Upgrade::CleanupBlockchainData() +bool Upgrade::CleanupBlockchainData(bool snapshotreq) { fs::path CleanupPath = GetDataDir(); @@ -421,7 +420,7 @@ bool Upgrade::CleanupBlockchainData() catch (fs::filesystem_error &ex) { - LogPrintf("Snapshot (CleanupBlockchainData): Exception occurred: %s", ex.what()); + LogPrintf("%s: Exception occurred: %s", snapshotreq ? "Snapshot (CleanupBlockchainData)" : "SyncFromZero (CleanupBlockchainData)", ex.what()); return false; } @@ -592,3 +591,8 @@ void Upgrade::DeleteSnapshot() LogPrintf("Snapshot Downloader: Exception occurred while attempting to delete snapshot (%s)", e.code().message()); } } + +bool Upgrade::SyncFromZero() +{ + return CleanupBlockchainData(false); +} diff --git a/src/gridcoin/upgrade.h b/src/gridcoin/upgrade.h index 65cf838560..9bdedf800a 100644 --- a/src/gridcoin/upgrade.h +++ b/src/gridcoin/upgrade.h @@ -45,7 +45,6 @@ class Upgrade //! //! \brief Scheduler call to CheckForLatestUpdate //! - static void ScheduledUpdateCheck(); //! @@ -64,7 +63,7 @@ class Upgrade //! //! \return Bool on the success of cleanup //! - static bool CleanupBlockchainData(); + static bool CleanupBlockchainData(bool snapshotreq = true); //! //! \brief Extracts the snapshot zip file @@ -91,6 +90,13 @@ class Upgrade //! \brief Small function to delete the snapshot.zip file //! static void DeleteSnapshot(); + + //! + //! \brief Small function to allow wallet user to clear blockchain data and sync from 0 while keeping a clean look + //! + //! \returns Bool on the success of blockchain cleanup + //! + static bool SyncFromZero(); }; //! diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index 8bf480bb76..ea42a6a586 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -99,6 +99,14 @@ bool AppInit(int argc, char* argv[]) // Initialize logging as early as possible. InitLogging(); + // Make sure a user does not request snapshotdownload and syncfromzero at same time! + if (mapArgs.count("-snapshotdownload") && mapArgs.count("-syncfromzero")) + { + fprintf(stderr, "-snapshotdownload and -syncfromzero cannot be used in conjunction"); + + exit(1); + } + // Check to see if the user requested a snapshot and we are not running TestNet! if (mapArgs.count("-snapshotdownload") && !mapArgs.count("-testnet")) { @@ -134,6 +142,33 @@ bool AppInit(int argc, char* argv[]) snapshot.DeleteSnapshot(); } + // Check to see if the user requested to sync from 0 -- We allow on testnet. + if (mapArgs.count("-syncfromzero")) + { + GRC::Upgrade syncfromzero; + + // Let's check make sure gridcoin is not already running in the data directory. + if (!LockDirectory(GetDataDir(), ".lock", false)) + { + fprintf(stderr, "Cannot obtain a lock on data directory %s. Gridcoin is probably already running.", GetDataDir().string().c_str()); + + exit(1); + } + + else + { + if (syncfromzero.SyncFromZero()) + LogPrintf("Syncfromzero: Success"); + + else + { + LogPrintf("Syncfromzero: Failed to clean up blockchain data"); + + exit(1); + } + } + } + LogPrintf("AppInit"); fRet = AppInit2(threads); diff --git a/src/init.cpp b/src/init.cpp index f3b276a3d1..1b5ac76630 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -309,7 +309,8 @@ std::string HelpMessage() " -snapshotsha256url= " + _("Optional: URL for the snapshot.sha256 file (ex: https://sub.domain.com/location/snapshot.sha256)") + "\n" " -disableupdatecheck " + _("Optional: Disable update checks by wallet") + "\n" " -updatecheckinterval= " + _("Optional: Check for updates every hours (default: 120, minimum: 1)") + "\n" - " -updatecheckurl= " + _("Optional: URL for the update version checks (ex: https://sub.domain.com/location/latest") + "\n"; + " -updatecheckurl= " + _("Optional: URL for the update version checks (ex: https://sub.domain.com/location/latest") + "\n" + " -syncfromzero " + _("Sync blockchain from zero. This argument will remove all previous blockchain data") + "\n"; return strUsage; } diff --git a/src/init.h b/src/init.h index 6cbc520265..13f9853387 100644 --- a/src/init.h +++ b/src/init.h @@ -25,4 +25,5 @@ std::string VersionMessage(); std::string LogSomething(); extern bool fSnapshotRequest; +extern bool fSyncfromzeroRequest; #endif diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 30ffb84997..ea12c70574 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -353,6 +353,14 @@ int main(int argc, char *argv[]) // Do this early as we don't want to bother initializing if we are just calling IPC ipcScanRelay(argc, argv); + // Make sure a user does not request snapshotdownload and syncfromzero at same time! + if (mapArgs.count("-snapshotdownload") && mapArgs.count("-syncfromzero")) + { + LogPrintf("-snapshotdownload and -syncfromzero cannot be used in conjunction"); + + return EXIT_FAILURE; + } + // Run snapshot main if Gridcoin was started with the snapshot argument and we are not TestNet if (mapArgs.count("-snapshotdownload") && !mapArgs.count("-testnet")) { @@ -376,6 +384,22 @@ int main(int argc, char *argv[]) snapshot.DeleteSnapshot(); } + // Check to see if the user requested to sync from 0 -- We allow on testnet. + if (mapArgs.count("-syncfromzero")) + { + GRC::Upgrade syncfromzero; + + if (syncfromzero.SyncFromZero()) + LogPrintf("Syncfromzero: Success"); + + else + { + LogPrintf("Syncfromzero: Failed to clean up blockchain data"); + + return EXIT_FAILURE; + } + } + /** Start Qt as normal before it was moved into this function **/ StartGridcoinQt(argc, argv, app, optionsModel); @@ -420,6 +444,28 @@ int main(int argc, char *argv[]) Snapshot.DeleteSnapshot(); } + // We received a request to remove blockchain data so client user can start to sync from 0 + if (fSyncfromzeroRequest) + { + UpgradeQt Syncfromzero; + + // Release LevelDB file handles on Windows so we can remove the old + // blockchain files: + // + // We should really close it in Shutdown() when the main application + // exits. Before we can do that, we need to solve an old outstanding + // conflict with the behavior of "-daemon" on Linux that prematurely + // closes the DB when the process forks. + // + CTxDB().Close(); + + if (Syncfromzero.SyncFromZero(app)) + LogPrintf("Syncfromzero: Success!"); + + else + LogPrintf("Syncfromzero: Failed!"); + } + return EXIT_SUCCESS; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f852cbb30e..6cc575d947 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -360,6 +360,8 @@ void BitcoinGUI::createActions() openRPCConsoleAction->setToolTip(tr("Open debugging and diagnostic console")); snapshotAction = new QAction(tr("&Snapshot Download"), this); snapshotAction->setToolTip(tr("Download and apply latest snapshot")); + syncfromzeroAction = new QAction(tr("Sync from &Zero"), this); + syncfromzeroAction->setToolTip(tr("Remove blockchain data and start chain from zero")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); @@ -375,6 +377,7 @@ void BitcoinGUI::createActions() connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); connect(diagnosticsAction, SIGNAL(triggered()), this, SLOT(diagnosticsClicked())); connect(snapshotAction, SIGNAL(triggered()), this, SLOT(snapshotClicked())); + connect(syncfromzeroAction, SIGNAL(triggered()), this, SLOT(syncfromzeroClicked())); } void BitcoinGUI::setIcons() @@ -409,6 +412,7 @@ void BitcoinGUI::setIcons() openRPCConsoleAction->setIcon(QPixmap(":/icons/debugwindow")); snapshotAction->setIcon(QPixmap(":/images/gridcoin")); openConfigAction->setIcon(QPixmap(":/icons/edit")); + syncfromzeroAction->setIcon(QPixmap(":/images/gridcoin")); } void BitcoinGUI::createMenuBar() @@ -434,6 +438,9 @@ void BitcoinGUI::createMenuBar() file->addAction(snapshotAction); } + file->addSeparator(); + file->addAction(syncfromzeroAction); + file->addSeparator(); file->addAction(quitAction); @@ -1040,6 +1047,41 @@ void BitcoinGUI::snapshotClicked() } } +void BitcoinGUI::syncfromzeroClicked() +{ + QMessageBox Msg; + + Msg.setIcon(QMessageBox::Question); + Msg.setText(tr("Do you wish to remove blockchain data and sync from zero.")); + Msg.setInformativeText(tr("Warning: Once removing blockchain data has been completed you will have to sync from 0 or use snapshot feature.")); + Msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + Msg.setDefaultButton(QMessageBox::No); + + int result = Msg.exec(); + bool fProceed; + + switch (result) + { + case QMessageBox::Yes : fProceed = true; break; + case QMessageBox::No : fProceed = false; break; + default : fProceed = false; break; + } + + if (!fProceed) + { + Msg.close(); + + return; + } + + else + { + fSyncfromzeroRequest = true; + + qApp->quit(); + } +} + void BitcoinGUI::diagnosticsClicked() { diagnosticsDialog->show(); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 214794953a..976bd65422 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -122,6 +122,7 @@ class BitcoinGUI : public QMainWindow QAction *lockWalletAction; QAction *openRPCConsoleAction; QAction *snapshotAction; + QAction *syncfromzeroAction; QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; @@ -225,6 +226,7 @@ private slots: void diagnosticsClicked(); void peersClicked(); void snapshotClicked(); + void syncfromzeroClicked(); #ifndef Q_OS_MAC /** Handle tray icon clicked */ diff --git a/src/qt/upgradeqt.cpp b/src/qt/upgradeqt.cpp index 89618790bb..a5937715a9 100644 --- a/src/qt/upgradeqt.cpp +++ b/src/qt/upgradeqt.cpp @@ -358,3 +358,17 @@ void UpgradeQt::DeleteSnapshot() LogPrintf("Snapshot Downloader: Exception occurred while attempting to delete snapshot (%s)", e.code().message()); } } + +bool UpgradeQt::SyncFromZero(QApplication& SyncfromzeroApp) +{ + SyncfromzeroApp.processEvents(); + SyncfromzeroApp.setWindowIcon(QPixmap(":/images/gridcoin")); + + Upgrade syncfromzero; + + bool fSuccess = syncfromzero.CleanupBlockchainData(false); + + Msg(_("Sync from zero: Blockchain data removal was a ") + (fSuccess ? _("Success") : _("Failure")), _("The wallet will now shutdown."), false); + + return fSuccess; +} diff --git a/src/qt/upgradeqt.h b/src/qt/upgradeqt.h index 2153c31772..979af75b52 100644 --- a/src/qt/upgradeqt.h +++ b/src/qt/upgradeqt.h @@ -66,6 +66,12 @@ class UpgradeQt //! \brief Small function to delete the snapshot.zip file //! static void DeleteSnapshot(); + //! + //! \brief Main function for sync from zero task. + //! + //! \return Returns success of blockchain data cleanup task. + //! + static bool SyncFromZero(QApplication& SyncfromzeroApp); }; #endif // UPGRADEQT_H From cae947c43ae4f23edb21f716bc9200f70a598eb9 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Fri, 2 Apr 2021 20:41:56 -0700 Subject: [PATCH 004/280] Use install_db4.sh in build document --- doc/build-unix.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/build-unix.md b/doc/build-unix.md index 3e0f0e13cb..662eac7c0d 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -64,6 +64,7 @@ These dependencies are required: libdb4.8 | Berkeley DB | Wallet storage (only needed when wallet enabled) qt | GUI | GUI toolkit (only needed when GUI enabled) libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled) + libzip | Zip Compression | For Zip Compression and Decompression for snapshot and scraper related functions For the versions used in the release, see [release-process.md](release-process.md) under *Fetch and build inputs*. @@ -81,7 +82,7 @@ Dependency Build Instructions: Ubuntu & Debian ---------------------------------------------- Build requirements: - sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils + sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils libzip-dev libfreetype-dev Options when installing required Boost library files: @@ -98,13 +99,22 @@ install necessary parts of boost: BerkeleyDB is required for the wallet. **For Ubuntu only:** db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin). + You can add the repository and install using the following commands: +**For Ubuntu 18.04 and earlier versions** + sudo apt-get install software-properties-common sudo add-apt-repository ppa:bitcoin/bitcoin sudo apt-get update sudo apt-get install libdb4.8-dev libdb4.8++-dev +**For Ubuntu 20.04+** + + For Ubuntu 20.04+ users the db4.8 is not available on the official PPA. Use the script in contrib/install_db4.sh + to compile and install db4.8. You can use the script in your build location. For example if your build + location is Gridcoin-Research/ then `./contrib/install_db4.sh $PWD` + Ubuntu and Debian have their own libdb-dev and libdb++-dev packages, but these will install BerkeleyDB 5.1 or later, which break binary wallet compatibility with the distributed executables which are based on BerkeleyDB 4.8. If you do not care about wallet compatibility, From a82160ded04ad43a0ab3ed66c3c2dc5c84f54060 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Sun, 4 Apr 2021 16:57:36 -0700 Subject: [PATCH 005/280] Requested changes and use ErrorMsg instead of Msg in qt side --- src/gridcoin/upgrade.cpp | 22 ++++++++++------------ src/gridcoin/upgrade.h | 2 +- src/gridcoinresearchd.cpp | 22 +++++++++++++++++++++- src/qt/bitcoin.cpp | 21 +++++++++++++++++++++ src/qt/bitcoingui.cpp | 4 ++-- src/qt/upgradeqt.cpp | 28 +++++++++++++++++++++++++--- 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index 405760ed34..64568da7ac 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -67,14 +67,12 @@ bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dial catch (const std::runtime_error& e) { - LogPrintf("%s: Exception occurred while checking for latest update. (%s)", UpdateCheckType, e.what()); - - return false; + return error("%s: Exception occurred while checking for latest update. (%s)", __func__, e.what()); } if (VersionResponse.empty()) { - LogPrintf("%s: No Response from github", UpdateCheckType); + LogPrintf("%s: No Response from github", __func__); return false; } @@ -99,7 +97,7 @@ bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dial catch (std::exception& ex) { - LogPrintf("%s: Exception occurred while parsing json response (%s)", UpdateCheckType, ex.what()); + LogPrintf("%s: Exception occurred while parsing json response (%s)", __func__, ex.what()); return false; } @@ -126,7 +124,7 @@ bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dial if (GithubVersion.size() != 4) { - LogPrintf("%s: Got malformed version (%s)", UpdateCheckType, GithubReleaseData); + LogPrintf("%s: Got malformed version (%s)", __func__, GithubReleaseData); return false; } @@ -152,7 +150,7 @@ bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dial } catch (std::exception& ex) { - LogPrintf("%s: Exception occurred checking client version against github version (%s)", UpdateCheckType, ToString(ex.what())); + LogPrintf("%s: Exception occurred checking client version against github version (%s)", __func__, ToString(ex.what())); return false; } @@ -190,11 +188,11 @@ void Upgrade::SnapshotMain() if (CheckForLatestUpdate(VersionResponse, false, true)) { - std::cout << _("Unable to perform a Snapshot download as the wallet has detected that a new mandatory version is available for download.") << std::endl; + std::cout << _("Unable to download a snapshot, as the wallet has detected that a new mandatory version is available for install. The mandatory upgrade must be installed before the snapshot can be downloaded and applied.") << std::endl; std::cout << _("Latest Version github data response:") << std::endl; std::cout << VersionResponse << std::endl; - throw std::runtime_error("Failed to download snapshot as mandatory client is available for download."); + throw std::runtime_error(_("Failed to download snapshot as mandatory client is available for download.")); } // Create a thread for snapshot to be downloaded @@ -365,7 +363,7 @@ bool Upgrade::VerifySHA256SUM() } } -bool Upgrade::CleanupBlockchainData(bool snapshotreq) +bool Upgrade::CleanupBlockchainData() { fs::path CleanupPath = GetDataDir(); @@ -420,7 +418,7 @@ bool Upgrade::CleanupBlockchainData(bool snapshotreq) catch (fs::filesystem_error &ex) { - LogPrintf("%s: Exception occurred: %s", snapshotreq ? "Snapshot (CleanupBlockchainData)" : "SyncFromZero (CleanupBlockchainData)", ex.what()); + LogPrintf("%s: Exception occurred: %s", __func__, ex.what()); return false; } @@ -594,5 +592,5 @@ void Upgrade::DeleteSnapshot() bool Upgrade::SyncFromZero() { - return CleanupBlockchainData(false); + return CleanupBlockchainData(); } diff --git a/src/gridcoin/upgrade.h b/src/gridcoin/upgrade.h index 9bdedf800a..16ba8bb625 100644 --- a/src/gridcoin/upgrade.h +++ b/src/gridcoin/upgrade.h @@ -63,7 +63,7 @@ class Upgrade //! //! \return Bool on the success of cleanup //! - static bool CleanupBlockchainData(bool snapshotreq = true); + static bool CleanupBlockchainData(); //! //! \brief Extracts the snapshot zip file diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index ea42a6a586..c2eb0ba8d6 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -142,7 +142,7 @@ bool AppInit(int argc, char* argv[]) snapshot.DeleteSnapshot(); } - // Check to see if the user requested to sync from 0 -- We allow on testnet. + // Check to see if the user requested to sync from 0 -- We allow sync from zero on testnet, but not a snapshot download. if (mapArgs.count("-syncfromzero")) { GRC::Upgrade syncfromzero; @@ -164,6 +164,26 @@ bool AppInit(int argc, char* argv[]) { LogPrintf("Syncfromzero: Failed to clean up blockchain data"); + std::string inftext = ""; + // Little more work then needed but we should be translation friendly imo + inftext.append(_("Sync from zero: Blockchain data removal was a Failure")); + inftext.append("\r\n\r\n"); + inftext.append(_("Datadir: ")); + inftext.append(GetDataDir().string()); + inftext.append("\r\n\r\n"); + inftext.append(_("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet.")); + inftext.append("\r\n"); + inftext.append(_("Failure to do so will result in undefined behaviour or failure to start wallet.")); + inftext.append("\r\n\r\n"); + inftext.append(_("You will need to delete the following.")); + inftext.append("\r\n\r\n"); + inftext.append(_("Files:")); + inftext.append("\r\nblk000*.dat\r\n\r\n"); + inftext.append(_("Directories:")); + inftext.append("\r\ntxleveldb\r\naccrual\r\n"); + + fprintf(stderr, "%s", inftext.c_str()); + exit(1); } } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ea12c70574..502ecbed81 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -396,6 +396,27 @@ int main(int argc, char *argv[]) { LogPrintf("Syncfromzero: Failed to clean up blockchain data"); + std::string inftext = ""; + // Little more work then needed but we should be translation friendly imo + inftext.append(_("Sync from zero: Blockchain data removal was a Failure")); + inftext.append("\r\n\r\n"); + inftext.append(_("Datadir: ")); + inftext.append(GetDataDir().string()); + inftext.append("\r\n\r\n"); + inftext.append(_("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet.")); + inftext.append("\r\n"); + inftext.append(_("Failure to do so will result in undefined behaviour or failure to start wallet.")); + inftext.append("\r\n\r\n"); + inftext.append(_("You will need to delete the following.")); + inftext.append("\r\n\r\n"); + inftext.append(_("Files:")); + inftext.append("\r\nblk000*.dat\r\n\r\n"); + inftext.append(_("Directories:")); + inftext.append("\r\ntxleveldb\r\naccrual\r\n"); + + ThreadSafeMessageBox(inftext, _("Gridcoin"), CClientUIInterface::OK | CClientUIInterface::MODAL); + QMessageBox::critical(nullptr, PACKAGE_NAME, QString::fromStdString(inftext)); + return EXIT_FAILURE; } } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6cc575d947..d2fab8ef2b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1052,8 +1052,8 @@ void BitcoinGUI::syncfromzeroClicked() QMessageBox Msg; Msg.setIcon(QMessageBox::Question); - Msg.setText(tr("Do you wish to remove blockchain data and sync from zero.")); - Msg.setInformativeText(tr("Warning: Once removing blockchain data has been completed you will have to sync from 0 or use snapshot feature.")); + Msg.setText(tr("Do you want to delete blockchain data and sync from zero?")); + Msg.setInformativeText(tr("Warning: After the blockchain data is deleted the wallet will shutdown and when restarted will begin syncing from zero. After restart you may continue syncing from zero or use the snapshot download to download the latest snapshot. Your balance will temporarily show as 0 GRC while syncing.")); Msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); Msg.setDefaultButton(QMessageBox::No); diff --git a/src/qt/upgradeqt.cpp b/src/qt/upgradeqt.cpp index a5937715a9..f9fe30972d 100644 --- a/src/qt/upgradeqt.cpp +++ b/src/qt/upgradeqt.cpp @@ -37,7 +37,7 @@ bool UpgradeQt::SnapshotMain(QApplication& SnapshotApp) QMessageBox Msg; Msg.setIcon(QMessageBox::Critical); - Msg.setText(ToQString(_("Unable to perform a Snapshot download as the wallet has detected that a new mandatory version is available for download."))); + Msg.setText(ToQString(_("Unable to download a snapshot, as the wallet has detected that a new mandatory version is available for install. The mandatory upgrade must be installed before the snapshot can be downloaded and applied."))); Msg.setInformativeText(ToQString(_("Latest Version github data response:\r\n") + VersionResponse)); Msg.setStandardButtons(QMessageBox::Ok); @@ -366,9 +366,31 @@ bool UpgradeQt::SyncFromZero(QApplication& SyncfromzeroApp) Upgrade syncfromzero; - bool fSuccess = syncfromzero.CleanupBlockchainData(false); + bool fSuccess = syncfromzero.CleanupBlockchainData(); - Msg(_("Sync from zero: Blockchain data removal was a ") + (fSuccess ? _("Success") : _("Failure")), _("The wallet will now shutdown."), false); + if (fSuccess) + Msg(_("Sync from zero: Blockchain data removal was a Success"), _("The wallet will now shutdown. Please start your wallet to begin sync from zero"), false); + + else + { + std::string inftext = ""; + // Little more work then needed but we should be translation friendly imo + inftext.append(_("Datadir: ")); + inftext.append(GetDataDir().string()); + inftext.append("\r\n\r\n"); + inftext.append(_("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet.")); + inftext.append("\r\n"); + inftext.append(_("Failure to do so will result in undefined behaviour or failure to start wallet.")); + inftext.append("\r\n\r\n"); + inftext.append(_("You will need to delete the following.")); + inftext.append("\r\n\r\n"); + inftext.append(_("Files:")); + inftext.append("\r\nblk000*.dat\r\n\r\n"); + inftext.append(_("Directories:")); + inftext.append("\r\ntxleveldb\r\naccrual"); + + ErrorMsg(_("Sync from zero: Blockchain data removal was a Failure"), inftext); + } return fSuccess; } From 21982ec477652655b49a7c12f25551ee41395f81 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 4 Apr 2021 21:27:23 -0400 Subject: [PATCH 006/280] Increment version to 5.3.1.1. --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0fd9f8fcdf..d32cdc0fbc 100755 --- a/configure.ac +++ b/configure.ac @@ -3,8 +3,8 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 3) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 0) -define(_CLIENT_VERSION_IS_RELEASE, true) +define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Gridcoin]]) From 4f4a4b400204ab23148865f965e7b407c27eeecb Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Mon, 1 Feb 2021 20:12:07 +0300 Subject: [PATCH 007/280] move CTransaction to primitives --- src/Makefile.am | 9 + src/consensus/consensus.h | 6 +- src/gridcoin/tx_message.cpp | 27 ++ src/gridcoin/tx_message.h | 6 + src/gridcoin/voting/registry.cpp | 3 +- src/index/disktxpos.h | 65 +++ src/index/txindex.h | 66 +++ src/main.cpp | 673 +---------------------------- src/main.h | 703 +------------------------------ src/miner.cpp | 20 +- src/policy/fees.cpp | 50 +++ src/policy/fees.h | 23 + src/primitives/transaction.cpp | 128 ++++++ src/primitives/transaction.h | 331 +++++++++++++++ src/qt/bitcoin.cpp | 4 +- src/qt/coincontroldialog.cpp | 4 +- src/qt/transactiondesc.cpp | 3 +- src/rpc/blockchain.cpp | 2 +- src/rpc/blockchain.h | 1 + src/rpc/rawtransaction.cpp | 13 +- src/test/script_p2sh_tests.cpp | 13 +- src/test/transaction_tests.cpp | 8 +- src/txdb-leveldb.cpp | 9 +- src/validation.cpp | 617 +++++++++++++++++++++++++++ src/validation.h | 113 +++++ src/wallet/wallet.cpp | 9 +- src/wallet/walletdb.cpp | 2 +- 27 files changed, 1518 insertions(+), 1390 deletions(-) create mode 100644 src/gridcoin/tx_message.cpp create mode 100644 src/index/disktxpos.h create mode 100644 src/index/txindex.h create mode 100644 src/policy/fees.cpp create mode 100644 src/policy/fees.h create mode 100644 src/primitives/transaction.cpp create mode 100644 src/primitives/transaction.h create mode 100644 src/validation.cpp create mode 100644 src/validation.h diff --git a/src/Makefile.am b/src/Makefile.am index 0a975fdd9b..b1a81b528e 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -127,6 +127,8 @@ GRIDCOIN_CORE_H = \ gridcoin/voting/result.h \ gridcoin/voting/vote.h \ hash.h \ + index/disktxpos.h \ + index/txindex.h \ init.h \ key.h \ keystore.h \ @@ -137,7 +139,9 @@ GRIDCOIN_CORE_H = \ netbase.h \ net.h \ pbkdf2.h \ + policy/fees.h \ prevector.h \ + primitives/transaction.h \ protocol.h \ reverselock.h \ rpc/blockchain.h \ @@ -167,6 +171,7 @@ GRIDCOIN_CORE_H = \ util/threadnames.h \ util/time.h \ util.h \ + validation.h \ version.h \ wallet/coincontrol.h \ wallet/db.h \ @@ -207,6 +212,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ gridcoin/superblock.cpp \ gridcoin/support/block_finder.cpp \ gridcoin/tally.cpp \ + gridcoin/tx_message.cpp \ gridcoin/upgrade.cpp \ gridcoin/voting/builders.cpp \ gridcoin/voting/claims.cpp \ @@ -224,6 +230,8 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ net.cpp \ noui.cpp \ pbkdf2.cpp \ + policy/fees.cpp \ + primitives/transaction.cpp \ protocol.cpp \ rpc/blockchain.cpp \ rpc/client.cpp \ @@ -249,6 +257,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ util/threadnames.cpp \ util/time.cpp \ util.cpp \ + validation.cpp \ version.cpp \ wallet/db.cpp \ wallet/rpcdump.cpp \ diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index cc617b689f..8bef36a363 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -1,5 +1,7 @@ #pragma once +#include "amount.h" + static const int LAST_POW_BLOCK = 2050; static const int CONSENSUS_LOOKBACK = 5; //Amount of blocks to go back from best block, to avoid counting forked blocks static const int BLOCK_GRANULARITY = 10; //Consensus block divisor @@ -20,6 +22,6 @@ static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; /** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; /** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ -static const int64_t MIN_TX_FEE = 10000; +static const CAmount MIN_TX_FEE = 10000; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ -static const int64_t MIN_RELAY_TX_FEE = MIN_TX_FEE; +static const CAmount MIN_RELAY_TX_FEE = MIN_TX_FEE; diff --git a/src/gridcoin/tx_message.cpp b/src/gridcoin/tx_message.cpp new file mode 100644 index 0000000000..adde3b1356 --- /dev/null +++ b/src/gridcoin/tx_message.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2014-2020 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "gridcoin/support/xml.h" +#include "gridcoin/tx_message.h" + +std::string GetMessage(const CTransaction& tx) +{ + if (tx.nVersion <= 1) { + return ExtractXML(tx.hashBoinc, "", ""); + } + + if (tx.vContracts.empty()) { + return std::string(); + } + + if (tx.vContracts.front().m_type != GRC::ContractType::MESSAGE) { + return std::string(); + } + + const auto payload = tx.vContracts.front().SharePayloadAs(); + + return payload->m_message; +} diff --git a/src/gridcoin/tx_message.h b/src/gridcoin/tx_message.h index 5d0bf732db..9be5172ed5 100644 --- a/src/gridcoin/tx_message.h +++ b/src/gridcoin/tx_message.h @@ -5,6 +5,7 @@ #pragma once #include "gridcoin/contract/payload.h" +#include "primitives/transaction.h" #include @@ -103,3 +104,8 @@ class TxMessage : public IContractPayload } }; // TxMessage } + +//! +//! \brief Get the custom, user-supplied transaction message, if any. +//! +std::string GetMessage(const CTransaction& tx); diff --git a/src/gridcoin/voting/registry.cpp b/src/gridcoin/voting/registry.cpp index 39db39e18c..dc24f89464 100644 --- a/src/gridcoin/voting/registry.cpp +++ b/src/gridcoin/voting/registry.cpp @@ -10,6 +10,7 @@ #include "gridcoin/voting/vote.h" #include "txdb.h" #include "ui_interface.h" +#include "validation.h" using namespace GRC; using LogFlags = BCLog::LogFlags; @@ -149,7 +150,7 @@ class PollClaimValidator CTransaction tx; - if (!tx.ReadFromDisk(tx_index.pos)) { + if (!ReadTxFromDisk(tx, tx_index.pos)) { throw InvalidPollError(); } diff --git a/src/index/disktxpos.h b/src/index/disktxpos.h new file mode 100644 index 0000000000..455e981a7c --- /dev/null +++ b/src/index/disktxpos.h @@ -0,0 +1,65 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_INDEX_DISKTXPOS_H +#define BITCOIN_INDEX_DISKTXPOS_H + +#include +#include "tinyformat.h" + +/** Position on disk for a particular transaction. */ +class CDiskTxPos +{ +public: + unsigned int nFile; + unsigned int nBlockPos; + unsigned int nTxPos; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nFile); + READWRITE(nBlockPos); + READWRITE(nTxPos); + } + + CDiskTxPos() + { + SetNull(); + } + + CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) + { + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nTxPos = nTxPosIn; + } + + void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; } + bool IsNull() const { return (nFile == (unsigned int) -1); } + + friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) + { + return (a.nFile == b.nFile && + a.nBlockPos == b.nBlockPos && + a.nTxPos == b.nTxPos); + } + + friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) + { + return !(a == b); + } + + std::string ToString() const + { + if (IsNull()) + return "null"; + else + return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos); + } +}; + +#endif // BITCOIN_INDEX_DISKTXPOS_H diff --git a/src/index/txindex.h b/src/index/txindex.h new file mode 100644 index 0000000000..53228ea378 --- /dev/null +++ b/src/index/txindex.h @@ -0,0 +1,66 @@ +// Copyright (c) 2017-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_INDEX_TXINDEX_H +#define BITCOIN_INDEX_TXINDEX_H + +/** A txdb record that contains the disk location of a transaction and the + * locations of transactions that spend its outputs. vSpent is really only + * used as a flag, but having the location is very helpful for debugging. + */ +class CTxIndex +{ +public: + CDiskTxPos pos; + std::vector vSpent; + + CTxIndex() + { + SetNull(); + } + + CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) + { + pos = posIn; + vSpent.resize(nOutputs); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + if (!(s.GetType() & SER_GETHASH)) { + int nVersion = s.GetVersion(); + READWRITE(nVersion); + } + + READWRITE(pos); + READWRITE(vSpent); + } + + void SetNull() + { + pos.SetNull(); + vSpent.clear(); + } + + bool IsNull() + { + return pos.IsNull(); + } + + friend bool operator==(const CTxIndex& a, const CTxIndex& b) + { + return (a.pos == b.pos && + a.vSpent == b.vSpent); + } + + friend bool operator!=(const CTxIndex& a, const CTxIndex& b) + { + return !(a == b); + } +}; + +#endif // BITCOIN_INDEX_TXINDEX_H diff --git a/src/main.cpp b/src/main.cpp index 3559712348..d4f49872be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,8 @@ #include "gridcoin/support/xml.h" #include "gridcoin/tally.h" #include "gridcoin/tx_message.h" +#include "policy/fees.h" +#include "validation.h" #include #include @@ -445,34 +447,6 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) // CTransaction and CTxIndex // -bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) -{ - SetNull(); - if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) - return false; - if (!ReadFromDisk(txindexRet.pos)) - return false; - if (prevout.n >= vout.size()) - { - SetNull(); - return false; - } - return true; -} - -bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) -{ - CTxIndex txindex; - return ReadFromDisk(txdb, prevout, txindex); -} - -bool CTransaction::ReadFromDisk(COutPoint prevout) -{ - CTxDB txdb("r"); - CTxIndex txindex; - return ReadFromDisk(txdb, prevout, txindex); -} - bool IsStandardTx(const CTransaction& tx) { @@ -576,84 +550,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } -// -// Check transaction inputs, and make sure any -// pay-to-script-hash transactions are evaluating IsStandard scripts -// -// Why bother? To avoid denial-of-service attacks; an attacker -// can submit a standard HASH... OP_EQUAL transaction, -// which will get accepted into blocks. The redemption -// script can be anything; an attacker could use a very -// expensive-to-check-upon-redemption script like: -// DUP CHECKSIG DROP ... repeated 100 times... OP_1 -// -bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const -{ - if (IsCoinBase()) - return true; // Coinbases don't use vin normally - - for (unsigned int i = 0; i < vin.size(); i++) - { - const CTxOut& prev = GetOutputFor(vin[i], mapInputs); - - vector > vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) - return false; - int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); - if (nArgsExpected < 0) - return false; - - // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig the - // IsStandard() call returns false - vector > stack; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) return false; - - if (whichType == TX_SCRIPTHASH) - { - if (stack.empty()) - return false; - CScript subscript(stack.back().begin(), stack.back().end()); - vector > vSolutions2; - txnouttype whichType2; - if (!Solver(subscript, whichType2, vSolutions2)) - return false; - if (whichType2 == TX_SCRIPTHASH) - return false; - - int tmpExpected; - tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; - } - - return true; -} - -unsigned int CTransaction::GetLegacySigOpCount() const -{ - unsigned int nSigOps = 0; - for (auto const& txin : vin) - { - nSigOps += txin.scriptSig.GetSigOpCount(false); - } - for (auto const& txout : vout) - { - nSigOps += txout.scriptPubKey.GetSigOpCount(false); - } - return nSigOps; -} - int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { @@ -701,193 +597,6 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) } -bool CTransaction::CheckTransaction() const -{ - // Basic checks that don't depend on any context - if (vin.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); - if (vout.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); - // Size limits - don't count coinbase superblocks--we check this at the block level: - if (::GetSerializeSize(*this, (SER_NETWORK & SER_SKIPSUPERBLOCK), PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); - - // Check for negative or overflow output values - int64_t nValueOut = 0; - for (unsigned int i = 0; i < vout.size(); i++) - { - const CTxOut& txout = vout[i]; - if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) - return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); - if (txout.nValue < 0) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); - if (txout.nValue > MAX_MONEY) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); - nValueOut += txout.nValue; - if (!MoneyRange(nValueOut)) - return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); - } - // Check for duplicate inputs - set vInOutPoints; - for (auto const& txin : vin) - { - if (vInOutPoints.count(txin.prevout)) - return false; - vInOutPoints.insert(txin.prevout); - } - - if (IsCoinBase()) - { - if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size is invalid")); - } - else - { - for (auto const& txin : vin) - if (txin.prevout.IsNull()) - return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); - } - - return true; -} - -bool CTransaction::CheckContracts(const MapPrevTx& inputs) const -{ - if (nVersion <= 1) { - return true; - } - - // Although v2 transactions support multiple contracts, we just allow one - // for now to mitigate spam: - if (GetContracts().size() > 1) { - return DoS(100, error("%s: only one contract allowed in tx", __func__)); - } - - if ((IsCoinBase() || IsCoinStake())) { - return DoS(100, error("%s: contract in non-standard tx", __func__)); - } - - int64_t required_burn_fee = 0; - - for (const auto& contract : GetContracts()) { - if (contract.m_version <= 1) { - return DoS(100, error("%s: legacy contract", __func__)); - } - - if (!contract.WellFormed()) { - return DoS(100, error("%s: malformed contract", __func__)); - } - - // Reject any transactions with administrative contracts sent from a - // wallet that does not hold the master key: - if (contract.RequiresMasterKey() && !HasMasterKeyInput(inputs)) { - return DoS(100, error("%s: contract requires master key", __func__)); - } - - required_burn_fee += contract.RequiredBurnAmount(); - } - - int64_t supplied_burn_fee = 0; - - for (const auto& output : vout) { - if (output.scriptPubKey[0] == OP_RETURN) { - supplied_burn_fee += output.nValue; - } - } - - if (supplied_burn_fee < required_burn_fee) { - return DoS(100, error( - "%s: insufficient burn output. Required: %s, supplied: %s", - __func__, - FormatMoney(required_burn_fee), - FormatMoney(supplied_burn_fee))); - } - - return true; -} - -bool CTransaction::HasMasterKeyInput(const MapPrevTx& inputs) const -{ - const CTxDestination master_address = CWallet::MasterAddress().Get(); - - for (const auto& input : vin) { - const CTxOut& prev_out = GetOutputFor(input, inputs); - CTxDestination dest; - - if (!ExtractDestination(prev_out.scriptPubKey, dest)) { - continue; - } - - if (dest == master_address) { - return true; - } - } - - return false; -} - -std::string CTransaction::GetMessage() const -{ - if (nVersion <= 1) { - return ExtractXML(hashBoinc, "", ""); - } - - if (vContracts.empty()) { - return std::string(); - } - - if (vContracts.front().m_type != GRC::ContractType::MESSAGE) { - return std::string(); - } - - const auto payload = vContracts.front().SharePayloadAs(); - - return payload->m_message; -} - -int64_t CTransaction::GetBaseFee(enum GetMinFee_mode mode) const -{ - // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE - int64_t nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; - - // For block version 11 onwards, which corresponds to CTransaction::CURRENT_VERSION 2, - // a multiplier is used on top of MIN_TX_FEE and MIN_RELAY_TX_FEE - if (nVersion >= 2) - { - nBaseFee *= 10; - } - - return nBaseFee; -} - -int64_t CTransaction::GetMinFee(unsigned int nBlockSize, enum GetMinFee_mode mode, unsigned int nBytes) const -{ - int64_t nBaseFee = GetBaseFee(mode); - - unsigned int nNewBlockSize = nBlockSize + nBytes; - int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; - - // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 - if (nMinFee < nBaseFee) - { - for (auto const& txout : vout) - if (txout.nValue < CENT) - nMinFee = nBaseFee; - } - - // Raise the price as the block approaches full - if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) - { - if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) - return MAX_MONEY; - nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; -} - bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInputs) { AssertLockHeld(cs_main); @@ -899,7 +608,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput return tx.DoS(100, error("AcceptToMemoryPool : legacy transaction")); } - if (!tx.CheckTransaction()) + if (!CheckTransaction(tx)) return error("AcceptToMemoryPool : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction @@ -967,7 +676,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput MapPrevTx mapInputs; map mapUnused; bool fInvalid = false; - if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + if (!FetchInputs(tx, txdb, mapUnused, false, false, mapInputs, fInvalid)) { if (fInvalid) return error("AcceptToMemoryPool : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); @@ -977,18 +686,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput } // Check for non-standard pay-to-script-hash in inputs - if (!tx.AreInputsStandard(mapInputs) && !fTestNet) + if (!AreInputsStandard(tx, mapInputs) && !fTestNet) return error("AcceptToMemoryPool : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64_t nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + CAmount nFees = GetValueIn(tx, mapInputs) - tx.GetValueOut(); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block - int64_t txMinFee = tx.GetMinFee(1000, GMF_RELAY, nSize); + CAmount txMinFee = GetMinFee(tx, 1000, GMF_RELAY, nSize); if (nFees < txMinFee) return error("AcceptToMemoryPool : not enough fees %s, %" PRId64 " < %" PRId64 ", nSize %" PRId64, hash.ToString().c_str(), @@ -997,7 +706,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (nFees < tx.GetBaseFee(GMF_RELAY)) + if (nFees < GetBaseFee(tx, GMF_RELAY)) { static CCriticalSection cs; static double dFreeCount; @@ -1020,13 +729,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput } // Validate any contracts published in the transaction: - if (!tx.GetContracts().empty() && !tx.CheckContracts(mapInputs)) { + if (!tx.GetContracts().empty() && !CheckContracts(tx, mapInputs)) { return false; } // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) + if (!ConnectInputs(tx, txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) { LogPrint(BCLog::LogFlags::MEMPOOL, "WARNING: %s: Unable to Connect Inputs %s.", __func__, @@ -1202,22 +911,6 @@ bool CWalletTx::AcceptWalletTransaction() return AcceptWalletTransaction(txdb); } -int CTxIndex::GetDepthInMainChain() const -{ - // Read block header - CBlock block; - if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) - return 0; - // Find the block in the index - BlockMap::iterator mi = mapBlockIndex.find(block.GetHash(true)); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) - return 0; - return 1 + nBestHeight - pindex->nHeight; -} - // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) { @@ -1231,7 +924,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) } CTxDB txdb("r"); CTxIndex txindex; - if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) + if (ReadTxFromDisk(tx, txdb, COutPoint(hash, 0), txindex)) { CBlock block; if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) @@ -1332,141 +1025,6 @@ void static InvalidChainFound(CBlockIndex* pindexNew) DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime())); } -bool CTransaction::DisconnectInputs(CTxDB& txdb) -{ - // Relinquish previous transactions' spent pointers - if (!IsCoinBase()) - { - for (auto const& txin : vin) - { - COutPoint prevout = txin.prevout; - // Get prev txindex from disk - CTxIndex txindex; - if (!txdb.ReadTxIndex(prevout.hash, txindex)) - return error("DisconnectInputs() : ReadTxIndex failed"); - - if (prevout.n >= txindex.vSpent.size()) - return error("DisconnectInputs() : prevout.n out of range"); - - // Mark outpoint as not spent - txindex.vSpent[prevout.n].SetNull(); - - // Write back - if (!txdb.UpdateTxIndex(prevout.hash, txindex)) - return error("DisconnectInputs() : UpdateTxIndex failed"); - } - } - - // Remove transaction from index - // This can fail if a duplicate of this transaction was in a chain that got - // reorganized away. This is only possible if this transaction was completely - // spent, so erasing it would be a no-op anyway. - txdb.EraseTxIndex(*this); - - return true; -} - - -bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, - bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) -{ - // FetchInputs can return false either because we just haven't seen some inputs - // (in which case the transaction should be stored as an orphan) - // or because the transaction is malformed (in which case the transaction should - // be dropped). If tx is definitely invalid, fInvalid will be set to true. - fInvalid = false; - - if (IsCoinBase()) - return true; // Coinbase transactions have no inputs to fetch. - - for (unsigned int i = 0; i < vin.size(); i++) - { - COutPoint prevout = vin[i].prevout; - if (inputsRet.count(prevout.hash)) - continue; // Got it already - - // Read txindex - CTxIndex& txindex = inputsRet[prevout.hash].first; - bool fFound = true; - if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) - { - // Get txindex from current proposed changes - txindex = mapTestPool.find(prevout.hash)->second; - } - else - { - // Read txindex from txdb - fFound = txdb.ReadTxIndex(prevout.hash, txindex); - } - if (!fFound && (fBlock || fMiner)) - return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - - // Read txPrev - CTransaction& txPrev = inputsRet[prevout.hash].second; - if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) - { - // Get prev tx from single transactions in memory - if (!mempool.lookup(prevout.hash, txPrev)) - { - LogPrint(BCLog::LogFlags::VERBOSE, "FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10), prevout.hash.ToString().substr(0,10)); - return false; - } - if (!fFound) - txindex.vSpent.resize(txPrev.vout.size()); - } - else - { - // Get prev tx from disk - if (!txPrev.ReadFromDisk(txindex.pos)) - return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - } - } - - // Make sure all prevout.n indexes are valid: - for (unsigned int i = 0; i < vin.size(); i++) - { - const COutPoint prevout = vin[i].prevout; - assert(inputsRet.count(prevout.hash) != 0); - const CTxIndex& txindex = inputsRet[prevout.hash].first; - const CTransaction& txPrev = inputsRet[prevout.hash].second; - if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - { - // Revisit this if/when transaction replacement is implemented and allows - // adding inputs: - fInvalid = true; - return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); - } - } - - return true; -} - -const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const -{ - MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); - if (mi == inputs.end()) - throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); - - const CTransaction& txPrev = (mi->second).second; - if (input.prevout.n >= txPrev.vout.size()) - throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); - - return txPrev.vout[input.prevout.n]; -} - -int64_t CTransaction::GetValueIn(const MapPrevTx& inputs) const -{ - if (IsCoinBase()) - return 0; - - int64_t nResult = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - nResult += GetOutputFor(vin[i], inputs).nValue; - } - return nResult; - -} static void UpdateSyncTime(const CBlockIndex* const pindexBest) { @@ -1491,144 +1049,6 @@ bool OutOfSyncByAge() return GetAdjustedTime() - g_previous_block_time >= maxAge; } -unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const -{ - if (IsCoinBase()) - return 0; - - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - const CTxOut& prevout = GetOutputFor(vin[i], inputs); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); - } - return nSigOps; -} - -bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner) -{ - // Take over previous transactions' spent pointers - // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain - // fMiner is true when called from the internal bitcoin miner - // ... both are false when called from CTransaction::AcceptToMemoryPool - if (!IsCoinBase()) - { - int64_t nValueIn = 0; - int64_t nFees = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - COutPoint prevout = vin[i].prevout; - assert(inputs.count(prevout.hash) > 0); - CTxIndex& txindex = inputs[prevout.hash].first; - CTransaction& txPrev = inputs[prevout.hash].second; - - if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); - - // If prev is coinbase or coinstake, check that it's matured - if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) - for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev) - if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) - return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight); - - // ppcoin: check transaction timestamp - if (txPrev.nTime > nTime) - return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction")); - - // Check for negative or overflow input values - nValueIn += txPrev.vout[prevout.n].nValue; - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return DoS(100, error("ConnectInputs() : txin values out of range")); - - } - // The first loop above does all the inexpensive checks. - // Only if ALL inputs pass do we perform expensive ECDSA signature checks. - // Helps prevent CPU exhaustion attacks. - for (unsigned int i = 0; i < vin.size(); i++) - { - COutPoint prevout = vin[i].prevout; - assert(inputs.count(prevout.hash) > 0); - CTxIndex& txindex = inputs[prevout.hash].first; - CTransaction& txPrev = inputs[prevout.hash].second; - - // Check for conflicts (double-spend) - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. - if (!txindex.vSpent[prevout.n].IsNull()) - { - if (fMiner) - { - msMiningErrorsExcluded += " ConnectInputs() : " + GetHash().GetHex() + " used at " - + txindex.vSpent[prevout.n].ToString() + "; "; - return false; - } - if (!txindex.vSpent[prevout.n].IsNull()) - { - if (fTestNet && pindexBlock->nHeight < nGrandfather) - { - return fMiner ? false : true; - } - if (!fTestNet && pindexBlock->nHeight < nGrandfather) - { - return fMiner ? false : true; - } - - if (fMiner) return false; - return LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) ? error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().c_str(), txindex.vSpent[prevout.n].ToString().c_str()) : false; - } - - } - - // Skip ECDSA signature verification when connecting blocks (fBlock=true) - // before the last blockchain checkpoint. This is safe because block merkle hashes are - // still computed and checked, and any change will be caught at the next checkpoint. - - if (!(fBlock && (nBestHeight < Params().Checkpoints().GetHeight()))) - { - // Verify signature - if (!VerifySignature(txPrev, *this, i, 0)) - { - return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); - } - } - - // Mark outpoints as spent - txindex.vSpent[prevout.n] = posThisTx; - - // Write back - if (fBlock || fMiner) - { - mapTestPool[prevout.hash] = txindex; - } - } - - if (!IsCoinStake()) - { - if (nValueIn < GetValueOut()) - { - LogPrintf("ConnectInputs(): VALUE IN < VALUEOUT "); - return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); - } - - // Tally transaction fees - int64_t nTxFee = nValueIn - GetValueOut(); - if (nTxFee < 0) - return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); - - // enforce transaction fees for every block - if (nTxFee < GetMinFee()) - return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false; - - nFees += nTxFee; - if (!MoneyRange(nFees)) - return DoS(100, error("ConnectInputs() : nFees out of range")); - } - } - - return true; -} bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) { @@ -1636,7 +1056,7 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) bool bDiscTxFailed = false; for (int i = vtx.size()-1; i >= 0; i--) { - if (!vtx[i].DisconnectInputs(txdb)) + if (!DisconnectInputs(vtx[i], txdb)) { bDiscTxFailed = true; } @@ -1762,7 +1182,7 @@ bool GetCoinstakeAge(CTxDB& txdb, const CBlock& block, uint64_t& out_coin_age) // of the kernel verify the transaction timestamp and that the staked // inputs exist in the main chain. // - if (block.nVersion <= 9 && !block.vtx[1].GetCoinAge(txdb, out_coin_age)) { + if (block.nVersion <= 9 && !GetCoinAge(block.vtx[1], txdb, out_coin_age)) { return error("ConnectBlock[] : %s unable to get coin age for coinstake", block.vtx[1].GetHash().ToString().substr(0,10)); } @@ -2235,7 +1655,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) return false; } - nSigOps += tx.GetLegacySigOpCount(); + nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock[] : too many sigops")); @@ -2251,18 +1671,18 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) else { bool fInvalid; - if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) + if (!FetchInputs(tx, txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) return false; // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. - nSigOps += tx.GetP2SHSigOpCount(mapInputs); + nSigOps += GetP2SHSigOpCount(tx, mapInputs); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock[] : too many sigops")); - int64_t nTxValueIn = tx.GetValueIn(mapInputs); - int64_t nTxValueOut = tx.GetValueOut(); + CAmount nTxValueIn = GetValueIn(tx, mapInputs); + CAmount nTxValueOut = tx.GetValueOut(); nValueIn += nTxValueIn; nValueOut += nTxValueOut; if (!tx.IsCoinStake()) @@ -2305,7 +1725,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // Validate any contracts published in the transaction: if (!tx.GetContracts().empty()) { - if (!tx.CheckContracts(mapInputs)) { + if (!CheckContracts(tx, mapInputs)) { return false; } @@ -2316,7 +1736,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) } } - if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false)) + if (!ConnectInputs(tx, txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false)) return false; } @@ -2749,55 +2169,6 @@ bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew) return GridcoinServices(); } -// ppcoin: total coin age spent in transaction, in the unit of coin-days. -// Only those coins meeting minimum age requirement counts. As those -// transactions not in main chain are not currently indexed so we -// might not find out about their coin age. Older transactions are -// guaranteed to be in main chain by sync-checkpoint. This rule is -// introduced to help nodes establish a consistent view of the coin -// age (trust score) of competing branches. -bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const -{ - CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds - nCoinAge = 0; - - if (IsCoinBase()) - return true; - - for (auto const& txin : vin) - { - // First try finding the previous transaction in database - CBlockHeader header; - CTransaction txPrev; - - if (!GRC::ReadStakedInput(txdb, txin.prevout.hash, header, txPrev)) - { - return false; - } - - if (nTime < txPrev.nTime) - { - return false; // Transaction timestamp violation - } - - if (header.GetBlockTime() + nStakeMinAge > nTime) - { - continue; // only count coins meeting min age requirement - } - - int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; - bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT; - - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printcoinage")) - LogPrintf("coin age nValueIn=%" PRId64 " nTimeDiff=%d bnCentSecond=%s", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString()); - } - - CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printcoinage")) - LogPrintf("coin age bnCoinDay=%s", bnCoinDay.ToString()); - nCoinAge = bnCoinDay.getuint64(); - return true; -} bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProof) { @@ -2964,7 +2335,7 @@ bool CBlock::CheckBlock(int height1, bool fCheckPOW, bool fCheckMerkleRoot, bool // Check transactions for (auto const& tx : vtx) { - if (!tx.CheckTransaction()) + if (!CheckTransaction(tx)) return DoS(tx.nDoS, error("CheckBlock[] : CheckTransaction failed")); // ppcoin: check transaction timestamp @@ -2985,7 +2356,7 @@ bool CBlock::CheckBlock(int height1, bool fCheckPOW, bool fCheckMerkleRoot, bool unsigned int nSigOps = 0; for (auto const& tx : vtx) { - nSigOps += tx.GetLegacySigOpCount(); + nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock[] : out-of-bounds SigOpCount")); diff --git a/src/main.h b/src/main.h index 6c1f75430a..dfeca862bd 100644 --- a/src/main.h +++ b/src/main.h @@ -9,14 +9,18 @@ #include "arith_uint256.h" #include "chainparams.h" #include "consensus/consensus.h" +#include "index/disktxpos.h" +#include "index/txindex.h" #include "util.h" #include "net.h" #include "gridcoin/block_index.h" #include "gridcoin/contract/contract.h" #include "gridcoin/cpid.h" +#include "primitives/transaction.h" #include "sync.h" #include "script.h" #include "scrypt.h" +#include "validation.h" #include #include @@ -212,640 +216,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInputs); bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew); -/** Position on disk for a particular transaction. */ -class CDiskTxPos -{ -public: - unsigned int nFile; - unsigned int nBlockPos; - unsigned int nTxPos; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(nFile); - READWRITE(nBlockPos); - READWRITE(nTxPos); - } - - CDiskTxPos() - { - SetNull(); - } - - CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) - { - nFile = nFileIn; - nBlockPos = nBlockPosIn; - nTxPos = nTxPosIn; - } - - void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; } - bool IsNull() const { return (nFile == (unsigned int) -1); } - - friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) - { - return (a.nFile == b.nFile && - a.nBlockPos == b.nBlockPos && - a.nTxPos == b.nTxPos); - } - - friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) - { - return !(a == b); - } - - - std::string ToString() const - { - if (IsNull()) - return "null"; - else - return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos); - } - - void print() const - { - LogPrintf("%s", ToString()); - } -}; - - - -/** An inpoint - a combination of a transaction and an index n into its vin */ -class CInPoint -{ -public: - CTransaction* ptx; - unsigned int n; - - CInPoint() { SetNull(); } - CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = (unsigned int) -1; } - bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } -}; - - - -/** An outpoint - a combination of a transaction hash and an index n into its vout */ -class COutPoint -{ -public: - uint256 hash; - unsigned int n; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(hash); - READWRITE(n); - } - - COutPoint() { SetNull(); } - COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } - void SetNull() { hash.SetNull(); n = (unsigned int) -1; } - bool IsNull() const { return (hash.IsNull() && n == (unsigned int) -1); } - - friend bool operator<(const COutPoint& a, const COutPoint& b) - { - return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); - } - - friend bool operator==(const COutPoint& a, const COutPoint& b) - { - return (a.hash == b.hash && a.n == b.n); - } - - friend bool operator!=(const COutPoint& a, const COutPoint& b) - { - return !(a == b); - } - - std::string ToString() const - { - return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n); - } - - void print() const - { - LogPrintf("%s", ToString()); - } -}; - - - - -/** An input of a transaction. It contains the location of the previous - * transaction's output that it claims and a signature that matches the - * output's public key. - */ -class CTxIn -{ -public: - COutPoint prevout; - CScript scriptSig; - unsigned int nSequence; - - CTxIn() - { - nSequence = std::numeric_limits::max(); - } - - explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = prevoutIn; - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = COutPoint(hashPrevTx, nOut); - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); - } - - bool IsFinal() const - { - return (nSequence == std::numeric_limits::max()); - } - - friend bool operator==(const CTxIn& a, const CTxIn& b) - { - return (a.prevout == b.prevout && - a.scriptSig == b.scriptSig && - a.nSequence == b.nSequence); - } - - friend bool operator!=(const CTxIn& a, const CTxIn& b) - { - return !(a == b); - } - - std::string ToStringShort() const - { - return strprintf(" %s %d", prevout.hash.ToString(), prevout.n); - } - - std::string ToString() const - { - std::string str; - str += "CTxIn("; - str += prevout.ToString(); - if (prevout.IsNull()) - str += strprintf(", coinbase %s", HexStr(scriptSig)); - else - str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24)); - if (nSequence != std::numeric_limits::max()) - str += strprintf(", nSequence=%u", nSequence); - str += ")"; - return str; - } - - void print() const - { - LogPrintf("%s", ToString()); - } -}; - - - - -/** An output of a transaction. It contains the public key that the next input - * must be able to sign with to claim it. - */ -class CTxOut -{ -public: - int64_t nValue; - CScript scriptPubKey; - - CTxOut() - { - SetNull(); - } - - CTxOut(int64_t nValueIn, CScript scriptPubKeyIn) - { - nValue = nValueIn; - scriptPubKey = scriptPubKeyIn; - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(nValue); - READWRITE(scriptPubKey); - } - - void SetNull() - { - nValue = -1; - scriptPubKey.clear(); - } - - bool IsNull() - { - return (nValue == -1); - } - - void SetEmpty() - { - nValue = 0; - scriptPubKey.clear(); - } - - bool IsEmpty() const - { - return (nValue == 0 && scriptPubKey.empty()); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - friend bool operator==(const CTxOut& a, const CTxOut& b) - { - return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); - } - - friend bool operator!=(const CTxOut& a, const CTxOut& b) - { - return !(a == b); - } - - std::string ToStringShort() const - { - return strprintf(" out %s %s", FormatMoney(nValue), scriptPubKey.ToString(true)); - } - - std::string ToString() const - { - if (IsEmpty()) return "CTxOut(empty)"; - return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue), scriptPubKey.ToString()); - } - - void print() const - { - LogPrintf("%s", ToString()); - } -}; - - - - -enum GetMinFee_mode -{ - GMF_BLOCK, - GMF_RELAY, - GMF_SEND, -}; - -typedef std::map > MapPrevTx; - -/** The basic transaction that is broadcasted on the network and contained in - * blocks. A transaction can contain multiple inputs and outputs. - */ -class CTransaction -{ -public: - static const int CURRENT_VERSION = 2; - int nVersion; - unsigned int nTime; - std::vector vin; - std::vector vout; - unsigned int nLockTime; - - // Denial-of-service detection: - mutable int nDoS; - bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } - std::string hashBoinc; - std::vector vContracts; - - CTransaction() - { - SetNull(); - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(nVersion); - READWRITE(nTime); - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); - - if (nVersion >= 2) { - READWRITE(vContracts); - } else { - READWRITE(hashBoinc); - } - } - - void SetNull() - { - nVersion = CTransaction::CURRENT_VERSION; - nTime = GetAdjustedTime(); - vin.clear(); - vout.clear(); - nLockTime = 0; - nDoS = 0; // Denial-of-service prevention - hashBoinc = ""; - vContracts.clear(); - } - - bool IsNull() const - { - return (vin.empty() && vout.empty()); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsNewerThan(const CTransaction& old) const - { - if (vin.size() != old.vin.size()) - return false; - for (unsigned int i = 0; i < vin.size(); i++) - if (vin[i].prevout != old.vin[i].prevout) - return false; - - bool fNewer = false; - unsigned int nLowest = std::numeric_limits::max(); - for (unsigned int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != old.vin[i].nSequence) - { - if (vin[i].nSequence <= nLowest) - { - fNewer = false; - nLowest = vin[i].nSequence; - } - if (old.vin[i].nSequence < nLowest) - { - fNewer = true; - nLowest = old.vin[i].nSequence; - } - } - } - return fNewer; - } - - bool IsCoinBase() const - { - return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1); - } - - bool IsCoinStake() const - { - // ppcoin: the coin stake transaction is marked with the first output empty - return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); - } - - /** Check for standard transaction types - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return True if all inputs (scriptSigs) use only standard transaction forms - @see CTransaction::FetchInputs - */ - bool AreInputsStandard(const MapPrevTx& mapInputs) const; - - //! - //! \brief Determine whether the transaction contains an input spent by the - //! master key holder. - //! - //! \param inputs Map of the previous transactions with outputs spent by - //! this transaction to search for the master key address. - //! - //! \return \c true if at least one of the inputs from one of the previous - //! transactions comes from the master key address. - //! - bool HasMasterKeyInput(const MapPrevTx& inputs) const; - - /** Count ECDSA signature operations the old-fashioned (pre-0.6) way - @return number of sigops this transaction's outputs will produce when spent - @see CTransaction::FetchInputs - */ - unsigned int GetLegacySigOpCount() const; - - /** Count ECDSA signature operations in pay-to-script-hash inputs. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return maximum number of sigops required to validate this transaction's inputs - @see CTransaction::FetchInputs - */ - unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const; - - /** Amount of bitcoins spent by this transaction. - @return sum of all outputs (note: does not include fees) - */ - int64_t GetValueOut() const - { - int64_t nValueOut = 0; - for (auto const& txout : vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); - } - return nValueOut; - } - - - - - - /** Amount of bitcoins coming in to this transaction - Note that lightweight clients may not know anything besides the hash of previous transactions, - so may not be able to calculate this. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return Sum of value of all inputs (scriptSigs) - @see CTransaction::FetchInputs - */ - int64_t GetValueIn(const MapPrevTx& mapInputs) const; - - int64_t GetBaseFee(enum GetMinFee_mode mode=GMF_BLOCK) const; - - int64_t GetMinFee(unsigned int nBlockSize=1, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) const; - - bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) - { - CAutoFile filein(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); - - // Read transaction - if (fseek(filein.Get(), pos.nTxPos, SEEK_SET) != 0) - return error("CTransaction::ReadFromDisk() : fseek failed"); - - try { - filein >> *this; - } - catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - - // Return file pointer - if (pfileRet) - { - if (fseek(filein.Get(), pos.nTxPos, SEEK_SET) != 0) - return error("CTransaction::ReadFromDisk() : second fseek failed"); - *pfileRet = filein.release(); - } - return true; - } - - friend bool operator==(const CTransaction& a, const CTransaction& b) - { - return (a.nVersion == b.nVersion && - a.nTime == b.nTime && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); - } - - friend bool operator!=(const CTransaction& a, const CTransaction& b) - { - return !(a == b); - } - - std::string ToStringShort() const - { - std::string str; - str += strprintf("%s %s", GetHash().ToString(), IsCoinBase()? "base" : (IsCoinStake()? "stake" : "user")); - return str; - } - - std::string ToString() const - { - std::string str; - str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction"); - str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%" PRIszu ", vout.size=%" PRIszu ", nLockTime=%d)\n", - GetHash().ToString().substr(0,10), - nTime, - nVersion, - vin.size(), - vout.size(), - nLockTime); - for (unsigned int i = 0; i < vin.size(); i++) - str += " " + vin[i].ToString() + "\n"; - for (unsigned int i = 0; i < vout.size(); i++) - str += " " + vout[i].ToString() + "\n"; - return str; - } - - void print() const - { - LogPrintf("%s", ToString()); - } - - - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); - bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); - bool ReadFromDisk(COutPoint prevout); - bool DisconnectInputs(CTxDB& txdb); - - /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. - - @param[in] txdb Transaction database - @param[in] mapTestPool List of pending changes to the transaction index database - @param[in] fBlock True if being called to add a new best-block to the chain - @param[in] fMiner True if being called by CreateNewBlock - @param[out] inputsRet Pointers to this transaction's inputs - @param[out] fInvalid returns true if transaction is invalid - @return Returns true if all inputs are in txdb or mapTestPool - */ - bool FetchInputs(CTxDB& txdb, const std::map& mapTestPool, - bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid); - - /** Sanity check previous transactions, then, if all checks succeed, - mark them as spent by this transaction. - - @param[in] inputs Previous transactions (from FetchInputs) - @param[out] mapTestPool Keeps track of inputs that need to be updated on disk - @param[in] posThisTx Position of this transaction on disk - @param[in] pindexBlock - @param[in] fBlock true if called from ConnectBlock - @param[in] fMiner true if called from CreateNewBlock - @return Returns true if all checks succeed - */ - bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs, - std::map& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner); - - bool CheckTransaction() const; - - //! - //! \brief Check the validity of any contracts contained in the transaction. - //! - //! \param inputs Map of the previous transactions with outputs spent by - //! this transaction to search for the master key address for validating - //! administrative contracts. - //! - //! \return \c true if all of the contracts in the transaction validate. - //! - bool CheckContracts(const MapPrevTx& inputs) const; - - //! - //! \brief Get the contracts contained in the transaction. - //! - //! \return The set of contracts contained in the transaction. Version 1 - //! transactions can only store one contract. - //! - const std::vector& GetContracts() const - { - if (nVersion == 1 && vContracts.empty() && GRC::Contract::Detect(hashBoinc)) { - REF(vContracts).emplace_back(GRC::Contract::Parse(hashBoinc)); - } - - return vContracts; - } - - //! - //! \brief Move the contracts contained in the transaction. - //! - //! \return The set of contracts contained in the transaction. - //! - std::vector PullContracts() - { - GetContracts(); // Populate vContracts for legacy transactions. - - return std::move(vContracts); - } - - //! - //! \brief Get the custom, user-supplied transaction message, if any. - //! - std::string GetMessage() const; - - bool GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const; // ppcoin: get transaction coin age - -protected: - const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const; -}; /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms @@ -913,71 +283,6 @@ class CMerkleTx : public CTransaction - -/** A txdb record that contains the disk location of a transaction and the - * locations of transactions that spend its outputs. vSpent is really only - * used as a flag, but having the location is very helpful for debugging. - */ -class CTxIndex -{ -public: - CDiskTxPos pos; - std::vector vSpent; - - CTxIndex() - { - SetNull(); - } - - CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) - { - pos = posIn; - vSpent.resize(nOutputs); - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - if (!(s.GetType() & SER_GETHASH)) { - int nVersion = s.GetVersion(); - READWRITE(nVersion); - } - - READWRITE(pos); - READWRITE(vSpent); - } - - void SetNull() - { - pos.SetNull(); - vSpent.clear(); - } - - bool IsNull() - { - return pos.IsNull(); - } - - friend bool operator==(const CTxIndex& a, const CTxIndex& b) - { - return (a.pos == b.pos && - a.vSpent == b.vSpent); - } - - friend bool operator!=(const CTxIndex& a, const CTxIndex& b) - { - return !(a == b); - } - int GetDepthInMainChain() const; - -}; - - - - - /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block diff --git a/src/miner.cpp b/src/miner.cpp index 4ca3f90837..4369c04ccc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -18,7 +18,9 @@ #include "gridcoin/staking/reward.h" #include "gridcoin/staking/status.h" #include "gridcoin/tally.h" +#include "policy/fees.h" #include "util.h" +#include "validation.h" #include "wallet/wallet.h" #include @@ -282,7 +284,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) // a transaction spammer can cheaply fill blocks using // 1-satoshi-fee transactions. It should be set above the real // cost to you of processing a transaction. - int64_t nMinTxFee = CoinBase.GetBaseFee(); + int64_t nMinTxFee = GetBaseFee(CoinBase); if (mapArgs.count("-mintxfee")) ParseMoney(mapArgs["-mintxfee"], nMinTxFee); @@ -332,7 +334,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) LogPrint(BCLog::LogFlags::NOISY, "Enumerating tx %s ",tx.GetHash().GetHex()); - if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + if (!ReadTxFromDisk(txPrev, txdb, txin.prevout, txindex)) { // This should never happen; all transactions in the memory // pool should connect to either transactions in the chain @@ -370,7 +372,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; nTotalIn += nValueIn; - int nConf = txindex.GetDepthInMainChain(); + int nConf = GetDepthInMainChain(txindex); dPriority += (double)nValueIn * nConf; } if (fMissingInputs) continue; @@ -429,7 +431,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) } // Legacy limits on sigOps: - unsigned int nTxSigOps = tx.GetLegacySigOpCount(); + unsigned int nTxSigOps = GetLegacySigOpCount(tx); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { msMiningErrorsExcluded += tx.GetHash().GetHex() + ":LegacySigOpLimit(" + @@ -447,7 +449,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) } // Transaction fee - int64_t nMinFee = tx.GetMinFee(nBlockSize, GMF_BLOCK); + CAmount nMinFee = GetMinFee(tx, nBlockSize, GMF_BLOCK); // Skip free transactions if we're past the minimum block size: if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) @@ -468,14 +470,14 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) map mapTestPoolTmp(mapTestPool); MapPrevTx mapInputs; bool fInvalid; - if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) + if (!FetchInputs(tx, txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) { LogPrint(BCLog::LogFlags::NOISY, "Unable to fetch inputs for tx %s ", tx.GetHash().GetHex()); msMiningErrorsExcluded += tx.GetHash().GetHex() + ":UnableToFetchInputs;"; continue; } - int64_t nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + CAmount nTxFees = GetValueIn(tx, mapInputs) - tx.GetValueOut(); if (nTxFees < nMinFee) { LogPrint(BCLog::LogFlags::NOISY, @@ -486,7 +488,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) continue; } - nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); + nTxSigOps += GetP2SHSigOpCount(tx, mapInputs); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { LogPrint(BCLog::LogFlags::NOISY, "Not including tx %s due to exceeding max sigops of %d, sigops is %d", @@ -498,7 +500,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) continue; } - if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) + if (!ConnectInputs(tx, txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) { LogPrint(BCLog::LogFlags::NOISY, "Unable to connect inputs for tx %s ",tx.GetHash().GetHex()); msMiningErrorsExcluded += tx.GetHash().GetHex() + ":UnableToConnectInputs();"; diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp new file mode 100644 index 0000000000..399e5d1d1b --- /dev/null +++ b/src/policy/fees.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "consensus/consensus.h" +#include "policy/fees.h" + +CAmount GetBaseFee(const CTransaction& tx, enum GetMinFee_mode mode) +{ + // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE + CAmount nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + + // For block version 11 onwards, which corresponds to CTransaction::CURRENT_VERSION 2, + // a multiplier is used on top of MIN_TX_FEE and MIN_RELAY_TX_FEE + if (tx.nVersion >= 2) + { + nBaseFee *= 10; + } + + return nBaseFee; +} + +CAmount GetMinFee(const CTransaction& tx, unsigned int nBlockSize, enum GetMinFee_mode mode, unsigned int nBytes) +{ + CAmount nBaseFee = GetBaseFee(tx, mode); + + unsigned int nNewBlockSize = nBlockSize + nBytes; + CAmount nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; + + // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 + if (nMinFee < nBaseFee) + { + for (auto const& txout : tx.vout) + if (txout.nValue < CENT) + nMinFee = nBaseFee; + } + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return MAX_MONEY; + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } + + if (!MoneyRange(nMinFee)) + nMinFee = MAX_MONEY; + return nMinFee; +} diff --git a/src/policy/fees.h b/src/policy/fees.h new file mode 100644 index 0000000000..96cf94e2f9 --- /dev/null +++ b/src/policy/fees.h @@ -0,0 +1,23 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POLICY_FEES_H +#define BITCOIN_POLICY_FEES_H + +#include "amount.h" +#include "primitives/transaction.h" + +enum GetMinFee_mode +{ + GMF_BLOCK, + GMF_RELAY, + GMF_SEND, +}; + +CAmount GetBaseFee(const CTransaction& tx, enum GetMinFee_mode mode=GMF_BLOCK); + +CAmount GetMinFee(const CTransaction& tx, unsigned int nBlockSize=1, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0); + +#endif // BITCOIN_POLICY_FEES_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp new file mode 100644 index 0000000000..3b316473e5 --- /dev/null +++ b/src/primitives/transaction.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + + +std::string COutPoint::ToString() const +{ + return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n); +} + + +std::string CTxIn::ToStringShort() const +{ + return strprintf(" %s %d", prevout.hash.ToString(), prevout.n); +} + + +std::string CTxIn::ToString() const +{ + std::string str; + str += "CTxIn("; + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig)); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24)); + if (nSequence != std::numeric_limits::max()) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; +} + + +std::string CTxOut::ToStringShort() const +{ + return strprintf(" out %s %s", FormatMoney(nValue), scriptPubKey.ToString(true)); +} + + +std::string CTxOut::ToString() const +{ + if (IsEmpty()) return "CTxOut(empty)"; + return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue), scriptPubKey.ToString()); +} + + +std::string CTransaction::ToStringShort() const +{ + std::string str; + str += strprintf("%s %s", GetHash().ToString(), IsCoinBase()? "base" : (IsCoinStake()? "stake" : "user")); + return str; +} + + +std::string CTransaction::ToString() const +{ + std::string str; + str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction"); + str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%" PRIszu ", vout.size=%" PRIszu ", nLockTime=%d)\n", + GetHash().ToString().substr(0,10), + nTime, + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; +} + + +void CTransaction::print() const +{ + LogPrintf("%s", ToString()); +} + + +bool CTransaction::IsNewerThan(const CTransaction& old) const +{ + if (vin.size() != old.vin.size()) + return false; + for (unsigned int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = std::numeric_limits::max(); + for (unsigned int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; +} + + +const std::vector& CTransaction::GetContracts() const +{ + if (nVersion == 1 && vContracts.empty() && GRC::Contract::Detect(hashBoinc)) { + REF(vContracts).emplace_back(GRC::Contract::Parse(hashBoinc)); + } + + return vContracts; +} + + +std::vector CTransaction::PullContracts() +{ + GetContracts(); // Populate vContracts for legacy transactions. + + return std::move(vContracts); +} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h new file mode 100644 index 0000000000..8de080272f --- /dev/null +++ b/src/primitives/transaction.h @@ -0,0 +1,331 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_PRIMITIVES_TRANSACTION_H +#define BITCOIN_PRIMITIVES_TRANSACTION_H + +#include "amount.h" +#include "gridcoin/contract/contract.h" +#include "script.h" +#include "serialize.h" + +/** An inpoint - a combination of a transaction and an index n into its vin */ +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = (unsigned int) -1; } + bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } +}; + + + +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(hash); + READWRITE(n); + } + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + void SetNull() { hash.SetNull(); n = (unsigned int) -1; } + bool IsNull() const { return (hash.IsNull() && n == (unsigned int) -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + + + + +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = std::numeric_limits::max(); + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + } + + bool IsFinal() const + { + return (nSequence == std::numeric_limits::max()); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToStringShort() const; + std::string ToString() const; +}; + + +/** An output of a transaction. It contains the public key that the next input + * must be able to sign with to claim it. + */ +class CTxOut +{ +public: + CAmount nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(CAmount nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nValue); + READWRITE(scriptPubKey); + } + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() + { + return (nValue == -1); + } + + void SetEmpty() + { + nValue = 0; + scriptPubKey.clear(); + } + + bool IsEmpty() const + { + return (nValue == 0 && scriptPubKey.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToStringShort() const; + std::string ToString() const; +}; + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +public: + static const int CURRENT_VERSION = 2; + int nVersion; + unsigned int nTime; + std::vector vin; + std::vector vout; + unsigned int nLockTime; + + // Denial-of-service detection: + mutable int nDoS; + bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } + std::string hashBoinc; + std::vector vContracts; + + CTransaction() + { + SetNull(); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + + if (nVersion >= 2) { + READWRITE(vContracts); + } else { + READWRITE(hashBoinc); + } + } + + void SetNull() + { + nVersion = CTransaction::CURRENT_VERSION; + nTime = GetAdjustedTime(); + vin.clear(); + vout.clear(); + nLockTime = 0; + nDoS = 0; // Denial-of-service prevention + hashBoinc = ""; + vContracts.clear(); + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1); + } + + bool IsCoinStake() const + { + // ppcoin: the coin stake transaction is marked with the first output empty + return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty()); + } + + /** Amount of bitcoins spent by this transaction. + @return sum of all outputs (note: does not include fees) + */ + CAmount GetValueOut() const + { + CAmount nValueOut = 0; + for (auto const& txout : vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.nTime == b.nTime && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + bool IsNewerThan(const CTransaction& old) const; + + std::string ToStringShort() const; + std::string ToString() const; + void print() const; + + //! + //! \brief Get the contracts contained in the transaction. + //! + //! \return The set of contracts contained in the transaction. Version 1 + //! transactions can only store one contract. + //! + const std::vector& GetContracts() const; + + //! + //! \brief Move the contracts contained in the transaction. + //! + //! \return The set of contracts contained in the transaction. + //! + std::vector PullContracts(); + +}; + +#endif // BITCOIN_PRIMITIVES_TRANSACTION_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 8106370e29..c4117eab5f 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -24,7 +24,9 @@ #include "winshutdownmonitor.h" #include "gridcoin/upgrade.h" #include "gridcoin/gridcoin.h" +#include "policy/fees.h" #include "upgradeqt.h" +#include "validation.h" #include #include @@ -104,7 +106,7 @@ static bool ThreadSafeAskFee(int64_t nFeeRequired, const std::string& strCaption CTransaction txDummy; // Min Fee - nMinFee = txDummy.GetBaseFee(GMF_SEND); + nMinFee = GetBaseFee(txDummy, GMF_SEND); } if(nFeeRequired < nMinFee || nFeeRequired <= nTransactionFee || fDaemon) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 7680f2240f..b297a1a45f 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -6,6 +6,8 @@ #include "walletmodel.h" #include "addresstablemodel.h" #include "optionsmodel.h" +#include "policy/fees.h" +#include "validation.h" #include "wallet/coincontrol.h" #include @@ -488,7 +490,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) int64_t nFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); // Min Fee - int64_t nMinFee = txDummy.GetMinFee(1000, GMF_SEND, nBytes); + int64_t nMinFee = GetMinFee(txDummy, 1000, GMF_SEND, nBytes); nPayFee = max(nFee, nMinFee); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index a8fc5195a9..c5bafe901b 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -1,6 +1,7 @@ #include "transactiondesc.h" #include "clientmodel.h" #include "guiutil.h" +#include "gridcoin/tx_message.h" #include "bitcoinunits.h" #include "main.h" #include "wallet/wallet.h" @@ -320,7 +321,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, unsigned int vo else strHTML += "" + tr("Block Hash") + ": " + sHashBlock.c_str() + "
"; - const std::string tx_message = wtx.GetMessage(); + const std::string tx_message = GetMessage(wtx); if (!tx_message.empty()) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 150fd6de7b..efbefa5384 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -402,7 +402,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) CTransaction txDummy; // Min Fee - int64_t nMinFee = txDummy.GetBaseFee(GMF_SEND); + CAmount nMinFee = GetBaseFee(txDummy, GMF_SEND); if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < nMinFee) throw runtime_error( diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index b19ad7966b..7d748f0668 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -24,6 +24,7 @@ #include "gridcoin/support/block_finder.h" #include "gridcoin/tally.h" #include "gridcoin/tx_message.h" +#include "policy/fees.h" #include "util.h" namespace GRC diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 8dad69c0a9..84d54f8355 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -14,10 +14,13 @@ #include "gridcoin/support/block_finder.h" #include "gridcoin/tx_message.h" #include "gridcoin/voting/payloads.h" +#include "policy/fees.h" +#include "primitives/transaction.h" #include "protocol.h" #include "server.h" #include "streams.h" #include "txdb.h" +#include "validation.h" #include "wallet/coincontrol.h" #include "wallet/wallet.h" @@ -746,7 +749,7 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) int64_t nBytes = nBytesInputs + 2 * 34 + 10; // Min Fee - int64_t nMinFee = txDummy.GetMinFee(1000, GMF_SEND, nBytes); + int64_t nMinFee = GetMinFee(txDummy, 1000, GMF_SEND, nBytes); int64_t nFee = nTransactionFee * (1 + (int64_t) nBytes / 1000); @@ -939,7 +942,7 @@ UniValue consolidatemsunspent(const UniValue& params, bool fHelp) hash = block.vtx[i].GetHash(); // In case a fail here we can just continue thou it shouldn't happen - if (!tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) + if (!ReadTxFromDisk(tx, txdb, COutPoint(hash, 0), txindex)) continue; // Extract the address from the transaction @@ -1077,7 +1080,7 @@ UniValue consolidatemsunspent(const UniValue& params, bool fHelp) nEstHexSize = nVInHexSize + nSigHexSize + nVInPadding + 12; nBytes = nEstHexSize / 2; - nMinFee = rawtx.GetMinFee(1000, GMF_SEND, nBytes); + nMinFee = GetMinFee(rawtx, 1000, GMF_SEND, nBytes); nFee = nTransactionFee * (1 + nBytes / 1000); nTxFee = std::max(nMinFee, nFee); nOutput = nTotal - nTxFee; @@ -1201,7 +1204,7 @@ UniValue scanforunspent(const UniValue& params, bool fHelp) hash = block.vtx[i].GetHash(); // In case a fail here we can just continue thou it shouldn't happen - if (!tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) + if (!ReadTxFromDisk(tx, txdb, COutPoint(hash, 0), txindex)) continue; // Extract the address from the transaction @@ -1571,7 +1574,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // FetchInputs aborts on failure, so we go one at a time. tempTx.vin.push_back(mergedTx.vin[i]); - tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); + FetchInputs(tempTx, txdb, unused, false, false, mapPrevTx, fInvalid); // Copy results into mapPrevOut: for (auto const& txin : tempTx.vin) diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp index dde2c8e826..eafde91426 100755 --- a/src/test/script_p2sh_tests.cpp +++ b/src/test/script_p2sh_tests.cpp @@ -7,6 +7,7 @@ #include "main.h" #include "script.h" +#include "validation.h" #include "wallet/wallet.h" using namespace std; @@ -293,15 +294,15 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[2].prevout.hash = txFrom.GetHash(); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); - BOOST_CHECK(txTo.AreInputsStandard(mapInputs)); - BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(mapInputs), 1); + BOOST_CHECK(::AreInputsStandard(txTo, mapInputs)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, mapInputs), 1); // Make sure adding crap to the scriptSigs makes them non-standard: for (int i = 0; i < 3; i++) { CScript t = txTo.vin[i].scriptSig; txTo.vin[i].scriptSig = (CScript() << 11) + t; - BOOST_CHECK(!txTo.AreInputsStandard(mapInputs)); + BOOST_CHECK(!::AreInputsStandard(txTo, mapInputs)); txTo.vin[i].scriptSig = t; } @@ -316,11 +317,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd.vin[1].prevout.hash = txFrom.GetHash(); txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); - BOOST_CHECK(!txToNonStd.AreInputsStandard(mapInputs)); - BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(mapInputs), 11); + BOOST_CHECK(!::AreInputsStandard(txToNonStd, mapInputs)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd, mapInputs), 11); txToNonStd.vin[0].scriptSig.clear(); - BOOST_CHECK(!txToNonStd.AreInputsStandard(mapInputs)); + BOOST_CHECK(!::AreInputsStandard(txToNonStd, mapInputs)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 74574fc40a..d0cbb1c3a6 100755 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) CTransaction tx; stream >> tx; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(), strTest); + BOOST_CHECK_MESSAGE(CheckTransaction(tx), strTest); for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -140,7 +140,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) CTransaction tx; stream >> tx; - fValid = tx.CheckTransaction(); + fValid = CheckTransaction(tx); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -166,11 +166,11 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CDataStream stream(vch, SER_DISK, CLIENT_VERSION); CTransaction tx; stream >> tx; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(), "Simple deserialized transaction should be valid."); + BOOST_CHECK_MESSAGE(CheckTransaction(tx), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail tx.vin.push_back(tx.vin[0]); - BOOST_CHECK_MESSAGE(!tx.CheckTransaction(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!CheckTransaction(tx), "Transaction with duplicate txins should be invalid."); } // diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp index 7afb710563..574d50d076 100644 --- a/src/txdb-leveldb.cpp +++ b/src/txdb-leveldb.cpp @@ -17,6 +17,7 @@ #include "main.h" #include "ui_interface.h" #include "util.h" +#include "validation.h" using namespace std; using namespace boost; @@ -232,7 +233,7 @@ bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) tx.SetNull(); if (!ReadTxIndex(hash, txindex)) return false; - return (tx.ReadFromDisk(txindex.pos)); + return ReadTxFromDisk(tx, txindex.pos); } bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) @@ -494,7 +495,7 @@ bool CTxDB::LoadBlockIndex() { // either an error or a duplicate transaction CTransaction txFound; - if (!txFound.ReadFromDisk(txindex.pos)) + if (!ReadTxFromDisk(txFound, txindex.pos)) { LogPrintf("LoadBlockIndex() : *** cannot read mislocated transaction %s", hashTx.ToString()); pindexFork = pindex->pprev; @@ -524,12 +525,12 @@ bool CTxDB::LoadBlockIndex() if (nCheckLevel>5) { CTransaction txSpend; - if (!txSpend.ReadFromDisk(txpos)) + if (!ReadTxFromDisk(txSpend, txpos)) { LogPrintf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk", hashTx.ToString(), nOutput); pindexFork = pindex->pprev; } - else if (!txSpend.CheckTransaction()) + else if (!CheckTransaction(txSpend)) { LogPrintf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid", hashTx.ToString(), nOutput); pindexFork = pindex->pprev; diff --git a/src/validation.cpp b/src/validation.cpp new file mode 100644 index 0000000000..51f2ee23f9 --- /dev/null +++ b/src/validation.cpp @@ -0,0 +1,617 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "checkpoints.h" +#include "main.h" +#include "gridcoin/staking/kernel.h" +#include "policy/fees.h" +#include "serialize.h" +#include "txdb-leveldb.h" +#include "util.h" +#include "validation.h" +#include "wallet/wallet.h" + +#include + +bool ReadTxFromDisk(CTransaction& tx, CDiskTxPos pos, FILE** pfileRet) +{ + tx.SetNull(); + + CAutoFile filein(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("ReadTxFromDisk() : OpenBlockFile failed"); + + // Read transaction + if (fseek(filein.Get(), pos.nTxPos, SEEK_SET) != 0) + return error("ReadTxFromDisk() : fseek failed"); + + try { + filein >> tx; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Return file pointer + if (pfileRet) + { + if (fseek(filein.Get(), pos.nTxPos, SEEK_SET) != 0) + return error("ReadTxFromDisk() : second fseek failed"); + *pfileRet = filein.release(); + } + return true; +} + +bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) +{ + tx.SetNull(); + if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) + return false; + if (!ReadTxFromDisk(tx, txindexRet.pos)) + return false; + if (prevout.n >= tx.vout.size()) + { + tx.SetNull(); + return false; + } + return true; +} + +bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout) +{ + CTxIndex txindex; + return ReadTxFromDisk(tx, txdb, prevout, txindex); +} + +bool ReadTxFromDisk(CTransaction& tx, COutPoint prevout) +{ + CTxDB txdb("r"); + CTxIndex txindex; + return ReadTxFromDisk(tx, txdb, prevout, txindex); +} + +bool CheckTransaction(const CTransaction& tx) +{ + // Basic checks that don't depend on any context + if (tx.vin.empty()) + return tx.DoS(10, error("CheckTransaction() : vin empty")); + if (tx.vout.empty()) + return tx.DoS(10, error("CheckTransaction() : vout empty")); + // Size limits - don't count coinbase superblocks--we check this at the block level: + if (GetSerializeSize(tx, (SER_NETWORK & SER_SKIPSUPERBLOCK), PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return tx.DoS(100, error("CheckTransaction() : size limits failed")); + + // Check for negative or overflow output values + CAmount nValueOut = 0; + for (unsigned int i = 0; i < tx.vout.size(); i++) + { + const CTxOut& txout = tx.vout[i]; + if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake()) + return tx.DoS(100, error("CheckTransaction() : txout empty for user transaction")); + if (txout.nValue < 0) + return tx.DoS(100, error("CheckTransaction() : txout.nValue negative")); + if (txout.nValue > MAX_MONEY) + return tx.DoS(100, error("CheckTransaction() : txout.nValue too high")); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return tx.DoS(100, error("CheckTransaction() : txout total out of range")); + } + // Check for duplicate inputs + std::set vInOutPoints; + for (auto const& txin : tx.vin) + { + if (vInOutPoints.count(txin.prevout)) + return false; + vInOutPoints.insert(txin.prevout); + } + + if (tx.IsCoinBase()) + { + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) + return tx.DoS(100, error("CheckTransaction() : coinbase script size is invalid")); + } + else + { + for (auto const& txin : tx.vin) + if (txin.prevout.IsNull()) + return tx.DoS(10, error("CheckTransaction() : prevout is null")); + } + + return true; +} + +bool CheckContracts(CTransaction& tx, const MapPrevTx& inputs) +{ + if (tx.nVersion <= 1) { + return true; + } + + // Although v2 transactions support multiple contracts, we just allow one + // for now to mitigate spam: + if (tx.GetContracts().size() > 1) { + return tx.DoS(100, error("%s: only one contract allowed in tx", __func__)); + } + + if ((tx.IsCoinBase() || tx.IsCoinStake())) { + return tx.DoS(100, error("%s: contract in non-standard tx", __func__)); + } + + CAmount required_burn_fee = 0; + + for (const auto& contract : tx.GetContracts()) { + if (contract.m_version <= 1) { + return tx.DoS(100, error("%s: legacy contract", __func__)); + } + + if (!contract.WellFormed()) { + return tx.DoS(100, error("%s: malformed contract", __func__)); + } + + // Reject any transactions with administrative contracts sent from a + // wallet that does not hold the master key: + if (contract.RequiresMasterKey() && !HasMasterKeyInput(tx, inputs)) { + return tx.DoS(100, error("%s: contract requires master key", __func__)); + } + + required_burn_fee += contract.RequiredBurnAmount(); + } + + CAmount supplied_burn_fee = 0; + + for (const auto& output : tx.vout) { + if (output.scriptPubKey[0] == OP_RETURN) { + supplied_burn_fee += output.nValue; + } + } + + if (supplied_burn_fee < required_burn_fee) { + return tx.DoS(100, error( + "%s: insufficient burn output. Required: %s, supplied: %s", + __func__, + FormatMoney(required_burn_fee), + FormatMoney(supplied_burn_fee))); + } + + return true; +} + +const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) +{ + MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); + if (mi == inputs.end()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); + + const CTransaction& txPrev = (mi->second).second; + if (input.prevout.n >= txPrev.vout.size()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); + + return txPrev.vout[input.prevout.n]; +} + +CAmount GetValueIn(const CTransaction& tx, const MapPrevTx& inputs) +{ + if (tx.IsCoinBase()) + return 0; + + CAmount nResult = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + nResult += GetOutputFor(tx.vin[i], inputs).nValue; + } + return nResult; + +} + +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs) +{ + if (tx.IsCoinBase()) + return true; // Coinbases don't use vin normally + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut& prev = GetOutputFor(tx.vin[i], mapInputs); + + std::vector> vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); + if (nArgsExpected < 0) + return false; + + // Transactions with extra stuff in their scriptSigs are + // non-standard. Note that this EvalScript() call will + // be quick, because if there are any operations + // beside "push data" in the scriptSig the + // IsStandard() call returns false + std::vector> stack; + if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, 0)) return false; + + if (whichType == TX_SCRIPTHASH) + { + if (stack.empty()) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + std::vector> vSolutions2; + txnouttype whichType2; + if (!Solver(subscript, whichType2, vSolutions2)) + return false; + if (whichType2 == TX_SCRIPTHASH) + return false; + + int tmpExpected; + tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } + + if (stack.size() != (unsigned int)nArgsExpected) + return false; + } + + return true; +} + +bool HasMasterKeyInput(const CTransaction& tx, const MapPrevTx& inputs) +{ + const CTxDestination master_address = CWallet::MasterAddress().Get(); + + for (const auto& input : tx.vin) { + const CTxOut& prev_out = GetOutputFor(input, inputs); + CTxDestination dest; + + if (!ExtractDestination(prev_out.scriptPubKey, dest)) { + continue; + } + + if (dest == master_address) { + return true; + } + } + + return false; +} + +unsigned int GetLegacySigOpCount(const CTransaction& tx) +{ + unsigned int nSigOps = 0; + for (auto const& txin : tx.vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + for (auto const& txout : tx.vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} + +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const MapPrevTx& inputs) +{ + if (tx.IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut& prevout = GetOutputFor(tx.vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); + } + return nSigOps; +} + +bool DisconnectInputs(CTransaction& tx, CTxDB& txdb) +{ + // Relinquish previous transactions' spent pointers + if (!tx.IsCoinBase()) + { + for (auto const& txin : tx.vin) + { + COutPoint prevout = txin.prevout; + // Get prev txindex from disk + CTxIndex txindex; + if (!txdb.ReadTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : ReadTxIndex failed"); + + if (prevout.n >= txindex.vSpent.size()) + return error("DisconnectInputs() : prevout.n out of range"); + + // Mark outpoint as not spent + txindex.vSpent[prevout.n].SetNull(); + + // Write back + if (!txdb.UpdateTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : UpdateTxIndex failed"); + } + } + + // Remove transaction from index + // This can fail if a duplicate of this transaction was in a chain that got + // reorganized away. This is only possible if this transaction was completely + // spent, so erasing it would be a no-op anyway. + txdb.EraseTxIndex(tx); + + return true; +} + + +bool FetchInputs(CTransaction& tx, CTxDB& txdb, const std::map& mapTestPool, + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) +{ + // FetchInputs can return false either because we just haven't seen some inputs + // (in which case the transaction should be stored as an orphan) + // or because the transaction is malformed (in which case the transaction should + // be dropped). If tx is definitely invalid, fInvalid will be set to true. + fInvalid = false; + + if (tx.IsCoinBase()) + return true; // Coinbase transactions have no inputs to fetch. + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint prevout = tx.vin[i].prevout; + if (inputsRet.count(prevout.hash)) + continue; // Got it already + + // Read txindex + CTxIndex& txindex = inputsRet[prevout.hash].first; + bool fFound = true; + if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool.find(prevout.hash)->second; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", tx.GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + + // Read txPrev + CTransaction& txPrev = inputsRet[prevout.hash].second; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + if (!mempool.lookup(prevout.hash, txPrev)) + { + LogPrint(BCLog::LogFlags::VERBOSE, "FetchInputs() : %s mempool Tx prev not found %s", tx.GetHash().ToString().substr(0,10), prevout.hash.ToString().substr(0,10)); + return false; + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!ReadTxFromDisk(txPrev, txindex.pos)) + return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", tx.GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + } + + // Make sure all prevout.n indexes are valid: + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const COutPoint prevout = tx.vin[i].prevout; + assert(inputsRet.count(prevout.hash) != 0); + const CTxIndex& txindex = inputsRet[prevout.hash].first; + const CTransaction& txPrev = inputsRet[prevout.hash].second; + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + { + // Revisit this if/when transaction replacement is implemented and allows + // adding inputs: + fInvalid = true; + return tx.DoS(100, error("FetchInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " prev tx %s\n%s", tx.GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + } + } + + return true; +} + +bool ConnectInputs(CTransaction& tx, CTxDB& txdb, MapPrevTx inputs, std::map& mapTestPool, const CDiskTxPos& posThisTx, + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner) +{ + // Take over previous transactions' spent pointers + // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain + // fMiner is true when called from the internal bitcoin miner + // ... both are false when called from CTransaction::AcceptToMemoryPool + if (!tx.IsCoinBase()) + { + int64_t nValueIn = 0; + int64_t nFees = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint prevout = tx.vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; + + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + return tx.DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %" PRIszu " %" PRIszu " prev tx %s\n%s", tx.GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + + // If prev is coinbase or coinstake, check that it's matured + if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) + for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev) + if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) + return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight); + + // ppcoin: check transaction timestamp + if (txPrev.nTime > tx.nTime) + return tx.DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction")); + + // Check for negative or overflow input values + nValueIn += txPrev.vout[prevout.n].nValue; + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return tx.DoS(100, error("ConnectInputs() : txin values out of range")); + + } + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint prevout = tx.vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; + + // Check for conflicts (double-spend) + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. + if (!txindex.vSpent[prevout.n].IsNull()) + { + if (fMiner) + { + msMiningErrorsExcluded += " ConnectInputs() : " + tx.GetHash().GetHex() + " used at " + + txindex.vSpent[prevout.n].ToString() + "; "; + return false; + } + if (!txindex.vSpent[prevout.n].IsNull()) + { + if (fTestNet && pindexBlock->nHeight < nGrandfather) + { + return fMiner ? false : true; + } + if (!fTestNet && pindexBlock->nHeight < nGrandfather) + { + return fMiner ? false : true; + } + + if (fMiner) return false; + return LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) ? error("ConnectInputs() : %s prev tx already used at %s", tx.GetHash().ToString().c_str(), txindex.vSpent[prevout.n].ToString().c_str()) : false; + } + + } + + // Skip ECDSA signature verification when connecting blocks (fBlock=true) + // before the last blockchain checkpoint. This is safe because block merkle hashes are + // still computed and checked, and any change will be caught at the next checkpoint. + + if (!(fBlock && (nBestHeight < Params().Checkpoints().GetHeight()))) + { + // Verify signature + if (!VerifySignature(txPrev, tx, i, 0)) + { + return tx.DoS(100,error("ConnectInputs() : %s VerifySignature failed", tx.GetHash().ToString().substr(0,10).c_str())); + } + } + + // Mark outpoints as spent + txindex.vSpent[prevout.n] = posThisTx; + + // Write back + if (fBlock || fMiner) + { + mapTestPool[prevout.hash] = txindex; + } + } + + if (!tx.IsCoinStake()) + { + if (nValueIn < tx.GetValueOut()) + { + LogPrintf("ConnectInputs(): VALUE IN < VALUEOUT "); + return tx.DoS(100, error("ConnectInputs() : %s value in < value out", tx.GetHash().ToString().substr(0,10).c_str())); + } + + // Tally transaction fees + CAmount nTxFee = nValueIn - tx.GetValueOut(); + if (nTxFee < 0) + return tx.DoS(100, error("ConnectInputs() : %s nTxFee < 0", tx.GetHash().ToString().substr(0,10).c_str())); + + // enforce transaction fees for every block + if (nTxFee < GetMinFee(tx)) + return fBlock? tx.DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", tx.GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee(tx)).c_str(), FormatMoney(nTxFee).c_str())) : false; + + nFees += nTxFee; + if (!MoneyRange(nFees)) + return tx.DoS(100, error("ConnectInputs() : nFees out of range")); + } + } + + return true; +} + +// ppcoin: total coin age spent in transaction, in the unit of coin-days. +// Only those coins meeting minimum age requirement counts. As those +// transactions not in main chain are not currently indexed so we +// might not find out about their coin age. Older transactions are +// guaranteed to be in main chain by sync-checkpoint. This rule is +// introduced to help nodes establish a consistent view of the coin +// age (trust score) of competing branches. +bool GetCoinAge(const CTransaction& tx, CTxDB& txdb, uint64_t& nCoinAge) +{ + CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds + nCoinAge = 0; + + if (tx.IsCoinBase()) + return true; + + for (auto const& txin : tx.vin) + { + // First try finding the previous transaction in database + CBlockHeader header; + CTransaction txPrev; + + if (!GRC::ReadStakedInput(txdb, txin.prevout.hash, header, txPrev)) + { + return false; + } + + if (tx.nTime < txPrev.nTime) + { + return false; // Transaction timestamp violation + } + + if (header.GetBlockTime() + nStakeMinAge > tx.nTime) + { + continue; // only count coins meeting min age requirement + } + + CAmount nValueIn = txPrev.vout[txin.prevout.n].nValue; + bnCentSecond += CBigNum(nValueIn) * (tx.nTime - txPrev.nTime) / CENT; + + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printcoinage")) + LogPrintf("coin age nValueIn=%" PRId64 " nTimeDiff=%d bnCentSecond=%s", nValueIn, tx.nTime - txPrev.nTime, bnCentSecond.ToString()); + } + + CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printcoinage")) + LogPrintf("coin age bnCoinDay=%s", bnCoinDay.ToString()); + nCoinAge = bnCoinDay.getuint64(); + return true; +} + + +int GetDepthInMainChain(const CTxIndex& txi) +{ + // Read block header + CBlock block; + if (!block.ReadFromDisk(txi.pos.nFile, txi.pos.nBlockPos, false)) + return 0; + // Find the block in the index + BlockMap::iterator mi = mapBlockIndex.find(block.GetHash(true)); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + return 1 + nBestHeight - pindex->nHeight; +} diff --git a/src/validation.h b/src/validation.h new file mode 100644 index 0000000000..d9686773be --- /dev/null +++ b/src/validation.h @@ -0,0 +1,113 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_VALIDATION_H +#define BITCOIN_VALIDATION_H + +#include "amount.h" +#include "index/disktxpos.h" +#include "primitives/transaction.h" + +#include + +class CTxDB; +class CBlockHeader; + +typedef std::map> MapPrevTx; + +bool ReadTxFromDisk(CTransaction& tx, CDiskTxPos pos, FILE** pfileRet=NULL); +bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); +bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout); +bool ReadTxFromDisk(CTransaction& tx, COutPoint prevout); + +bool CheckTransaction(const CTransaction& tx); +//! \brief Check the validity of any contracts contained in the transaction. +//! +//! \param tx The transaction to check. +//! +//! \param inputs Map of the previous transactions with outputs spent by +//! tx to search for the master key address for validating administrative +//! contracts. +//! +//! \return \c true if all of the contracts in the transaction validate. +//! +bool CheckContracts(CTransaction& tx, const MapPrevTx& inputs); + +/** Check for standard transaction types + @param[in] tx Transaction to check + @param[in] mapInputs Map of previous transactions that have outputs tx is spending + @return True if all inputs (scriptSigs) use only standard transaction forms + @see FetchInputs +*/ +bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs); + +//! \brief Determine whether a transaction contains an input spent by the +//! master key holder. +//! +//! \param tx The transaction to check for. +//! +//! \param inputs Map of the previous transactions with outputs spent by +//! this transaction to search for the master key address. +//! +//! \return \c true if at least one of the inputs from one of the previous +//! transactions comes from the master key address. +//! +bool HasMasterKeyInput(const CTransaction& tx, const MapPrevTx& inputs); + +/** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @param[in] tx The transaction to count + @return number of sigops tx's outputs will produce when spent + @see FetchInputs +*/ +unsigned int GetLegacySigOpCount(const CTransaction& tx); +/** Count ECDSA signature operations in pay-to-script-hash inputs. + @param[in] tx The transaction to count + @param[in] mapInputs Map of previous transactions that have outputs tx is spending + @return maximum number of sigops required to validate tx's inputs + @see FetchInputs +*/ +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const MapPrevTx& inputs); + +const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs); +/** Amount of bitcoins coming in to a transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + @param[in] tx The transaction + @param[in] mapInputs Map of previous transactions that have outputs tx is spending + @return Sum of value of all inputs (scriptSigs) + @see FetchInputs +*/ +CAmount GetValueIn(const CTransaction& tx, const MapPrevTx& inputs); + +bool DisconnectInputs(CTransaction& tx, CTxDB& txdb); +/** Fetch from memory and/or disk. inputsRet keys are transaction hashes. + @param[in] tx The transaction to fetch inputs for + @param[in] txdb Transaction database + @param[in] mapTestPool List of pending changes to the transaction index database + @param[in] fBlock True if being called to add a new best-block to the chain + @param[in] fMiner True if being called by CreateNewBlock + @param[out] inputsRet Pointers to this tx's inputs + @param[out] fInvalid returns true if tx is invalid + @return Returns true if all inputs are in txdb or mapTestPool +*/ +bool FetchInputs(CTransaction& tx, CTxDB& txdb, const std::map& mapTestPool, bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid); +/** Sanity check previous transactions, then, if all checks succeed, + mark them as spent by tx. + @param[in] tx The transaction to connect inputs + @param[in] inputs Previous transactions (from FetchInputs) + @param[out] mapTestPool Keeps track of inputs that need to be updated on disk + @param[in] posThisTx Position of tx on disk + @param[in] pindexBlock + @param[in] fBlock true if called from ConnectBlock + @param[in] fMiner true if called from CreateNewBlock + @return Returns true if all checks succeed + */ +bool ConnectInputs(CTransaction& tx, CTxDB& txdb, MapPrevTx inputs, std::map& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock, bool fBlock, bool fMiner); + +bool GetCoinAge(const CTransaction& tx, CTxDB& txdb, uint64_t& nCoinAge); // ppcoin: get transaction coin age + +int GetDepthInMainChain(const CTxIndex& txi); + +#endif // BITCOIN_VALIDATION_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 09b5d4e01b..903aa26797 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -22,6 +22,7 @@ #include #include "gridcoin/staking/kernel.h" #include "gridcoin/support/block_finder.h" +#include "policy/fees.h" using namespace std; @@ -1222,7 +1223,7 @@ void CWallet::ResendWalletTransactions(bool fForce) for (auto const &item : mapSorted) { CWalletTx& wtx = *item.second; - if (wtx.CheckTransaction()) { + if (CheckTransaction(wtx)) { wtx.RelayWalletTransaction(txdb); } else { LogPrintf("ResendWalletTransactions() : CheckTransaction failed for transaction %s", wtx.GetHash().ToString()); @@ -1945,9 +1946,9 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // if sub-cent change is required, the fee must be raised to at least GetBaseFee // or until nChange becomes zero // NOTE: this depends on the exact behaviour of GetMinFee - if (nFeeRet < wtxNew.GetBaseFee() && nChange > 0 && nChange < CENT) + if (nFeeRet < GetBaseFee(wtxNew) && nChange > 0 && nChange < CENT) { - int64_t nMoveToFee = min(nChange, wtxNew.GetBaseFee() - nFeeRet); + int64_t nMoveToFee = min(nChange, GetBaseFee(wtxNew) - nFeeRet); nChange -= nMoveToFee; nFeeRet += nMoveToFee; @@ -2038,7 +2039,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Check that enough fee is included int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); - int64_t nMinFee = wtxNew.GetMinFee(1000, GMF_SEND, nBytes); + int64_t nMinFee = GetMinFee(wtxNew, 1000, GMF_SEND, nBytes); LogPrint(BCLog::LogFlags::ESTIMATEFEE, "INFO %s: nTransactionFee = %s, nBytes = %" PRId64 ", nPayFee = %s" ", nMinFee = %s, nFeeRet = %s.", diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index c286bf51bf..6bdd9930f6 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -219,7 +219,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> hash; CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; - if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) + if (CheckTransaction(wtx) && (wtx.GetHash() == hash)) wtx.BindWallet(pwallet); else { From a45b594c622f7ef91d6a15064f68bf85b9804054 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Mon, 5 Apr 2021 23:06:55 +0300 Subject: [PATCH 008/280] Port bitcoin's constant-space Merkle algorithm --- src/Makefile.am | 2 + src/consensus/merkle.cpp | 171 +++++++++++++++++++++++++++++++++++++++ src/consensus/merkle.h | 31 +++++++ 3 files changed, 204 insertions(+) create mode 100644 src/consensus/merkle.cpp create mode 100644 src/consensus/merkle.h diff --git a/src/Makefile.am b/src/Makefile.am index 0a975fdd9b..0e5066b9e4 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -73,6 +73,7 @@ GRIDCOIN_CORE_H = \ compat/byteswap.h \ compat/endian.h \ consensus/consensus.h \ + consensus/merkle.h \ consensus/params.h \ crypter.h \ fs.h \ @@ -182,6 +183,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ chainparams.cpp \ chainparamsbase.cpp \ checkpoints.cpp \ + consensus/merkle.cpp \ crypter.cpp \ fs.cpp \ gridcoin/appcache.cpp \ diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp new file mode 100644 index 0000000000..62b76388e5 --- /dev/null +++ b/src/consensus/merkle.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2015-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +/* WARNING! If you're reading this because you're learning about crypto + and/or designing a new system that will use merkle trees, keep in mind + that the following merkle tree algorithm has a serious flaw related to + duplicate txids, resulting in a vulnerability (CVE-2012-2459). + The reason is that if the number of hashes in the list at a given time + is odd, the last one is duplicated before computing the next level (which + is unusual in Merkle trees). This results in certain sequences of + transactions leading to the same merkle root. For example, these two + trees: + A A + / \ / \ + B C B C + / \ | / \ / \ + D E F D E F F + / \ / \ / \ / \ / \ / \ / \ + 1 2 3 4 5 6 1 2 3 4 5 6 5 6 + for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and + 6 are repeated) result in the same root hash A (because the hash of both + of (F) and (F,F) is C). + The vulnerability results from being able to send a block with such a + transaction list, with the same merkle root, and the same block hash as + the original without duplication, resulting in failed validation. If the + receiving node proceeds to mark that block as permanently invalid + however, it will fail to accept further unmodified (and thus potentially + valid) versions of the same block. We defend against this by detecting + the case where we would hash two identical hashes at the end of the list + together, and treating that identically to the block having an invalid + merkle root. Assuming no double-SHA256 collisions, this will detect all + known ways of changing the transactions without affecting the merkle + root. +*/ + +/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */ +static void MerkleComputation(const std::vector& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector* pbranch) { + if (pbranch) pbranch->clear(); + if (leaves.size() == 0) { + if (pmutated) *pmutated = false; + if (proot) *proot = uint256(); + return; + } + bool mutated = false; + // count is the number of leaves processed so far. + uint32_t count = 0; + // inner is an array of eagerly computed subtree hashes, indexed by tree + // level (0 being the leaves). + // For example, when count is 25 (11001 in binary), inner[4] is the hash of + // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to + // the last leaf. The other inner entries are undefined. + uint256 inner[32]; + // Which position in inner is a hash that depends on the matching leaf. + int matchlevel = -1; + // First process all leaves into 'inner' values. + while (count < leaves.size()) { + uint256 h = leaves[count]; + bool matchh = count == branchpos; + count++; + int level; + // For each of the lower bits in count that are 0, do 1 step. Each + // corresponds to an inner value that existed before processing the + // current leaf, and each needs a hash to combine it. + for (level = 0; !(count & (((uint32_t)1) << level)); level++) { + if (pbranch) { + if (matchh) { + pbranch->push_back(inner[level]); + } else if (matchlevel == level) { + pbranch->push_back(h); + matchh = true; + } + } + mutated |= (inner[level] == h); + CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin()); + } + // Store the resulting hash at inner position level. + inner[level] = h; + if (matchh) { + matchlevel = level; + } + } + // Do a final 'sweep' over the rightmost branch of the tree to process + // odd levels, and reduce everything to a single top value. + // Level is the level (counted from the bottom) up to which we've sweeped. + int level = 0; + // As long as bit number level in count is zero, skip it. It means there + // is nothing left at this level. + while (!(count & (((uint32_t)1) << level))) { + level++; + } + uint256 h = inner[level]; + bool matchh = matchlevel == level; + while (count != (((uint32_t)1) << level)) { + // If we reach this point, h is an inner value that is not the top. + // We combine it with itself (Bitcoin's special rule for odd levels in + // the tree) to produce a higher level one. + if (pbranch && matchh) { + pbranch->push_back(h); + } + CHash256().Write(h.begin(), 32).Write(h.begin(), 32).Finalize(h.begin()); + // Increment count to the value it would have if two entries at this + // level had existed. + count += (((uint32_t)1) << level); + level++; + // And propagate the result upwards accordingly. + while (!(count & (((uint32_t)1) << level))) { + if (pbranch) { + if (matchh) { + pbranch->push_back(inner[level]); + } else if (matchlevel == level) { + pbranch->push_back(h); + matchh = true; + } + } + CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin()); + level++; + } + } + // Return result. + if (pmutated) *pmutated = mutated; + if (proot) *proot = h; +} + +uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated) { + uint256 hash; + MerkleComputation(leaves, &hash, mutated, -1, nullptr); + return hash; +} + +std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position) { + std::vector ret; + MerkleComputation(leaves, nullptr, nullptr, position, &ret); + return ret; +} + +uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector& vMerkleBranch, uint32_t nIndex) { + uint256 hash = leaf; + for (std::vector::const_iterator it = vMerkleBranch.begin(); it != vMerkleBranch.end(); ++it) { + if (nIndex & 1) { + hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash)); + } else { + hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it)); + } + nIndex >>= 1; + } + return hash; +} + +uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) +{ + std::vector leaves; + leaves.resize(block.vtx.size()); + for (size_t s = 0; s < block.vtx.size(); s++) { + leaves[s] = block.vtx[s].GetHash(); + } + return ComputeMerkleRoot(leaves, mutated); +} + +std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) +{ + std::vector leaves; + leaves.resize(block.vtx.size()); + for (size_t s = 0; s < block.vtx.size(); s++) { + leaves[s] = block.vtx[s].GetHash(); + } + return ComputeMerkleBranch(leaves, position); +} diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h new file mode 100644 index 0000000000..bec2ec8c09 --- /dev/null +++ b/src/consensus/merkle.h @@ -0,0 +1,31 @@ +// Copyright (c) 2015-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_MERKLE +#define BITCOIN_MERKLE + +#include +#include + +#include "main.h" +#include + +uint256 ComputeMerkleRoot(const std::vector& leaves, bool* mutated = nullptr); +std::vector ComputeMerkleBranch(const std::vector& leaves, uint32_t position); +uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector& branch, uint32_t position); + +/* + * Compute the Merkle root of the transactions in a block. + * *mutated is set to true if a duplicated subtree was found. + */ +uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = nullptr); + +/* + * Compute the Merkle branch for the tree of transactions in a block, for a + * given position. + * This can be verified using ComputeMerkleRootFromBranch. + */ +std::vector BlockMerkleBranch(const CBlock& block, uint32_t position); + +#endif From 414c3eb1f5bd1d8318741e20f5a3240609c3c78a Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Mon, 5 Apr 2021 23:10:27 +0300 Subject: [PATCH 009/280] Add Merkle tests --- src/Makefile.test.include | 1 + src/test/merkle_tests.cpp | 173 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 src/test/merkle_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 771b8ce1c4..9242592eff 100755 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -52,6 +52,7 @@ GRIDCOIN_TESTS =\ test/gridcoin/researcher_tests.cpp \ test/gridcoin/superblock_tests.cpp \ test/key_tests.cpp \ + test/merkle_tests.cpp \ test/mruset_tests.cpp \ test/multisig_tests.cpp \ test/netbase_tests.cpp \ diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp new file mode 100644 index 0000000000..683279e801 --- /dev/null +++ b/src/test/merkle_tests.cpp @@ -0,0 +1,173 @@ +// Copyright (c) 2015-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +BOOST_AUTO_TEST_SUITE(merkle_tests) + +// Older version of the merkle root computation code, for comparison. +static uint256 BlockBuildMerkleTree(const CBlock& block, std::vector& vMerkleTree) +{ + vMerkleTree.clear(); + for (auto const& tx : block.vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); +} + + +static inline int ctz(uint32_t i) { + if (i == 0) return 0; + int j = 0; + while (!(i & 1)) { + j++; + i >>= 1; + } + return j; +} + +BOOST_AUTO_TEST_CASE(merkle_test) +{ + for (int i = 0; i < 32; i++) { + // Try 32 block sizes: all sizes from 0 to 16 inclusive, and then 15 random sizes. + int ntx = (i <= 16) ? i : 17 + (GetRand(4000)); + // Try up to 3 mutations. + for (int mutate = 0; mutate <= 3; mutate++) { + int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first. + if (duplicate1 >= ntx) break; // Duplication of the entire tree results in a different root (it adds a level). + int ntx1 = ntx + duplicate1; // The resulting number of transactions after the first duplication. + int duplicate2 = mutate >= 2 ? 1 << ctz(ntx1) : 0; // Likewise for the second mutation. + if (duplicate2 >= ntx1) break; + int ntx2 = ntx1 + duplicate2; + int duplicate3 = mutate >= 3 ? 1 << ctz(ntx2) : 0; // And for the third mutation. + if (duplicate3 >= ntx2) break; + int ntx3 = ntx2 + duplicate3; + // Build a block with ntx different transactions. + CBlock block; + block.vtx.resize(ntx); + for (int j = 0; j < ntx; j++) { + CTransaction tx; + tx.nLockTime = j; + block.vtx[j] = tx; + } + // Compute the root of the block before mutating it. + bool unmutatedMutated = false; + uint256 unmutatedRoot = BlockMerkleRoot(block, &unmutatedMutated); + BOOST_CHECK(unmutatedMutated == false); + // Optionally mutate by duplicating the last transactions, resulting in the same merkle root. + block.vtx.resize(ntx3); + for (int j = 0; j < duplicate1; j++) { + block.vtx[ntx + j] = block.vtx[ntx + j - duplicate1]; + } + for (int j = 0; j < duplicate2; j++) { + block.vtx[ntx1 + j] = block.vtx[ntx1 + j - duplicate2]; + } + for (int j = 0; j < duplicate3; j++) { + block.vtx[ntx2 + j] = block.vtx[ntx2 + j - duplicate3]; + } + // Compute the merkle root and merkle tree using the old mechanism. + std::vector merkleTree; + uint256 oldRoot = BlockBuildMerkleTree(block, merkleTree); + // Compute the merkle root using the new mechanism. + bool newMutated = false; + uint256 newRoot = BlockMerkleRoot(block, &newMutated); + BOOST_CHECK(oldRoot == newRoot); + BOOST_CHECK(newRoot == unmutatedRoot); + BOOST_CHECK((newRoot == uint256()) == (ntx == 0)); + BOOST_CHECK(newMutated == !!mutate); + } + } +} + + +BOOST_AUTO_TEST_CASE(merkle_test_empty_block) +{ + bool mutated = false; + CBlock block; + uint256 root = BlockMerkleRoot(block, &mutated); + + BOOST_CHECK_EQUAL(root.IsNull(), true); + BOOST_CHECK_EQUAL(mutated, false); +} + +BOOST_AUTO_TEST_CASE(merkle_test_oneTx_block) +{ + bool mutated = false; + CBlock block; + + block.vtx.resize(1); + CTransaction tx; + tx.nLockTime = 0; + block.vtx[0] = tx; + uint256 root = BlockMerkleRoot(block, &mutated); + BOOST_CHECK(root == block.vtx[0].GetHash()); + BOOST_CHECK_EQUAL(mutated, false); +} + +BOOST_AUTO_TEST_CASE(merkle_test_OddTxWithRepeatedLastTx_block) +{ + bool mutated; + CBlock block, blockWithRepeatedLastTx; + + block.vtx.resize(3); + + for (std::size_t pos = 0; pos < block.vtx.size(); pos++) { + CTransaction tx; + tx.nLockTime = pos; + block.vtx[pos] = tx; + } + + blockWithRepeatedLastTx = block; + blockWithRepeatedLastTx.vtx.push_back(blockWithRepeatedLastTx.vtx.back()); + + uint256 rootofBlock = BlockMerkleRoot(block, &mutated); + BOOST_CHECK_EQUAL(mutated, false); + + uint256 rootofBlockWithRepeatedLastTx = BlockMerkleRoot(blockWithRepeatedLastTx, &mutated); + BOOST_CHECK(rootofBlock == rootofBlockWithRepeatedLastTx); + BOOST_CHECK_EQUAL(mutated, true); +} + +BOOST_AUTO_TEST_CASE(merkle_test_LeftSubtreeRightSubtree) +{ + CBlock block, leftSubtreeBlock, rightSubtreeBlock; + + block.vtx.resize(4); + std::size_t pos; + for (pos = 0; pos < block.vtx.size(); pos++) { + CTransaction tx; + tx.nLockTime = pos; + block.vtx[pos] = tx; + } + + for (pos = 0; pos < block.vtx.size() / 2; pos++) + leftSubtreeBlock.vtx.push_back(block.vtx[pos]); + + for (pos = block.vtx.size() / 2; pos < block.vtx.size(); pos++) + rightSubtreeBlock.vtx.push_back(block.vtx[pos]); + + uint256 root = BlockMerkleRoot(block); + uint256 rootOfLeftSubtree = BlockMerkleRoot(leftSubtreeBlock); + uint256 rootOfRightSubtree = BlockMerkleRoot(rightSubtreeBlock); + std::vector leftRight; + leftRight.push_back(rootOfLeftSubtree); + leftRight.push_back(rootOfRightSubtree); + uint256 rootOfLR = ComputeMerkleRoot(leftRight); + + BOOST_CHECK(root == rootOfLR); +} + +BOOST_AUTO_TEST_SUITE_END() From 9ac3dfa6a8ad99461631f928ff80d5996cdb98b8 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Mon, 5 Apr 2021 23:13:08 +0300 Subject: [PATCH 010/280] Add mutation check and use the new algorithm --- src/chainparams.cpp | 3 +- src/init.cpp | 1 - src/main.cpp | 35 ++++++++++++--------- src/main.h | 70 +++-------------------------------------- src/miner.cpp | 4 +-- src/wallet/wallet.cpp | 3 +- src/wallet/walletdb.cpp | 13 ++++++++ src/wallet/walletdb.h | 14 ++------- 8 files changed, 46 insertions(+), 97 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ad932085c4..c16be32d7c 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -5,6 +5,7 @@ #include "chainparams.h" +#include "consensus/merkle.h" #include "tinyformat.h" #include "util/strencodings.h" @@ -31,7 +32,7 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, uint32_t nTime, uint3 genesis.nVersion = nVersion; genesis.vtx.push_back(txNew); genesis.hashPrevBlock.SetNull(); - genesis.hashMerkleRoot = genesis.BuildMerkleTree(); + genesis.hashMerkleRoot = BlockMerkleRoot(genesis); return genesis; } */ diff --git a/src/init.cpp b/src/init.cpp index e49148203f..66482ea393 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -929,7 +929,6 @@ bool AppInit2(ThreadHandlerPtr threads) CBlockIndex* pindex = (*mi).second; CBlock block; block.ReadFromDisk(pindex); - block.BuildMerkleTree(); block.print(); LogPrintf(""); nFound++; diff --git a/src/main.cpp b/src/main.cpp index 3559712348..7224c2a471 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "amount.h" +#include "consensus/merkle.h" #include "util.h" #include "net.h" #include "streams.h" @@ -680,15 +681,11 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) break; if (nIndex == (int)pblock->vtx.size()) { - vMerkleBranch.clear(); nIndex = -1; LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block"); return 0; } - // Fill in merkle branch - vMerkleBranch = pblock->GetMerkleBranch(nIndex); - // Is the tx in a block that's in the main chain BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) @@ -1140,14 +1137,6 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const if (!pindex || !pindex->IsInMainChain()) return 0; - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) - { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; - } - pindexRet = pindex; return pindexBest->nHeight - pindex->nHeight + 1; } @@ -2874,6 +2863,9 @@ bool CBlock::CheckBlock(int height1, bool fCheckPOW, bool fCheckMerkleRoot, bool GetHash(true) == (fTestNet ? hashGenesisBlockTestNet : hashGenesisBlock)) return true; + if (fChecked) + return true; + // These are checks that are independent of context // that can be verified before saving an orphan block. @@ -2991,8 +2983,21 @@ bool CBlock::CheckBlock(int height1, bool fCheckPOW, bool fCheckMerkleRoot, bool return DoS(100, error("CheckBlock[] : out-of-bounds SigOpCount")); // Check merkle root - if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) - return DoS(100, error("CheckBlock[] : hashMerkleRoot mismatch")); + if (fCheckMerkleRoot) { + bool mutated; + uint256 hashMerkleRoot2 = BlockMerkleRoot(*this, &mutated); + if (hashMerkleRoot != hashMerkleRoot2) + return DoS(100, error("CheckBlock[] : hashMerkleRoot mismatch")); + + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences + // of transactions in a block without affecting the merkle root of a block, + // while still invalidating it. + if (mutated) + return DoS(100, error("%s: duplicate transaction", __func__)); + } + + if (fCheckPOW && fCheckMerkleRoot && fCheckSig) + fChecked = true; return true; } @@ -3549,7 +3554,7 @@ bool LoadBlockIndex(bool fAllowNew) CBlock block; block.vtx.push_back(txNew); block.hashPrevBlock.SetNull(); - block.hashMerkleRoot = block.BuildMerkleTree(); + block.hashMerkleRoot = BlockMerkleRoot(block); block.nVersion = 1; //R&D - Testers Wanted Thread: block.nTime = !fTestNet ? 1413033777 : 1406674534; diff --git a/src/main.h b/src/main.h index 6c1f75430a..6dade33bd3 100644 --- a/src/main.h +++ b/src/main.h @@ -863,13 +863,8 @@ class CMerkleTx : public CTransaction int GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const; public: uint256 hashBlock; - std::vector vMerkleBranch; int nIndex; - // memory only - mutable bool fMerkleVerified; - - CMerkleTx() { Init(); @@ -884,7 +879,6 @@ class CMerkleTx : public CTransaction { hashBlock.SetNull(); nIndex = -1; - fMerkleVerified = false; } ADD_SERIALIZE_METHODS; @@ -892,9 +886,10 @@ class CMerkleTx : public CTransaction template inline void SerializationOp(Stream& s, Operation ser_action) { + std::vector dummy_vector1; //!< Used to be vMerkleBranch READWRITEAS(CTransaction, *this); READWRITE(hashBlock); - READWRITE(vMerkleBranch); + READWRITE(dummy_vector1); READWRITE(nIndex); } @@ -1114,7 +1109,7 @@ class CBlock : public CBlockHeader std::vector vchBlockSig; // memory only - mutable std::vector vMerkleTree; + mutable bool fChecked; // Denial-of-service detection: mutable int nDoS; @@ -1154,7 +1149,7 @@ class CBlock : public CBlockHeader vtx.clear(); vchBlockSig.clear(); - vMerkleTree.clear(); + fChecked = false; nDoS = 0; } @@ -1206,57 +1201,6 @@ class CBlock : public CBlockHeader return maxTransactionTime; } - uint256 BuildMerkleTree() const - { - vMerkleTree.clear(); - for (auto const& tx : vtx) - vMerkleTree.push_back(tx.GetHash()); - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - for (int i = 0; i < nSize; i += 2) - { - int i2 = std::min(i+1, nSize-1); - vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); - } - j += nSize; - } - return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); - } - - std::vector GetMerkleBranch(int nIndex) const - { - if (vMerkleTree.empty()) - BuildMerkleTree(); - std::vector vMerkleBranch; - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - int i = std::min(nIndex^1, nSize-1); - vMerkleBranch.push_back(vMerkleTree[j+i]); - nIndex >>= 1; - j += nSize; - } - return vMerkleBranch; - } - - static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) - { - if (nIndex == -1) - return uint256(); - for (auto const& otherside : vMerkleBranch) - { - if (nIndex & 1) - hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); - else - hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); - nIndex >>= 1; - } - return hash; - } - - bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) { // Open history file to append @@ -1326,9 +1270,6 @@ class CBlock : public CBlockHeader LogPrintf(" "); vtx[i].print(); } - LogPrintf(" vMerkleTree: "); - for (unsigned int i = 0; i < vMerkleTree.size(); i++) - LogPrintf("%s", vMerkleTree[i].ToString().substr(0,10)); } @@ -1783,9 +1724,8 @@ class CDiskBlockIndex : public CBlockIndex */ class CBlockLocator { -protected: - std::vector vHave; public: + std::vector vHave; CBlockLocator() { diff --git a/src/miner.cpp b/src/miner.cpp index 4ca3f90837..9951184e2f 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -5,6 +5,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "amount.h" +#include "consensus/merkle.h" #include "txdb.h" #include "miner.h" #include "main.h" @@ -1023,7 +1024,7 @@ bool SignStakeBlock(CBlock &block, CKey &key, vector &StakeInp } //Sign the whole block - block.hashMerkleRoot = block.BuildMerkleTree(); + block.hashMerkleRoot = BlockMerkleRoot(block); if( !key.Sign(block.GetHash(), block.vchBlockSig) ) { return error("SignStakeBlock: failed to sign block"); @@ -1441,4 +1442,3 @@ void StakeMiner(CWallet *pwallet) g_timer.GetTimes(function + "ProcessBlock", "miner"); } //end while(!fShutdown) } - diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 09b5d4e01b..fb26b19613 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -547,9 +547,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, CWalletDB* pwalletdb) wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + if (wtxIn.nIndex != -1 && wtxIn.nIndex != wtx.nIndex) { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; wtx.nIndex = wtxIn.nIndex; fUpdated = true; } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index c286bf51bf..667476dea1 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -31,6 +31,19 @@ bool CWalletDB::EraseName(const string& strAddress) return Erase(make_pair(string("name"), strAddress)); } +bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) +{ + nWalletDBUpdated++; + Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan + return Write(std::string("bestblock_nomerkle"), locator); +} + +bool CWalletDB::ReadBestBlock(CBlockLocator& locator) +{ + if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; + return Read(std::string("bestblock_nomerkle"), locator); +} + bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) { account.SetNull(); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index a8ce2e70f9..95875cc228 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -71,6 +71,9 @@ class CWalletDB : public CDB bool WriteName(const std::string& strAddress, const std::string& strName); bool EraseName(const std::string& strAddress); + + bool WriteBestBlock(const CBlockLocator& locator); + bool ReadBestBlock(CBlockLocator& locator); bool WriteTx(uint256 hash, const CWalletTx& wtx) { @@ -124,17 +127,6 @@ class CWalletDB : public CDB return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); } - bool WriteBestBlock(const CBlockLocator& locator) - { - nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); - } - - bool ReadBestBlock(CBlockLocator& locator) - { - return Read(std::string("bestblock"), locator); - } - bool WriteOrderPosNext(int64_t nOrderPosNext) { nWalletDBUpdated++; From 1fd971a540b3a1065ac4931932a793af6d515c56 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Tue, 6 Apr 2021 16:38:45 -0700 Subject: [PATCH 011/280] Changes --- src/gridcoin/upgrade.cpp | 32 ++++++++++++++++++++++++++++++-- src/gridcoin/upgrade.h | 7 +++++++ src/gridcoinresearchd.cpp | 18 +----------------- src/qt/bitcoin.cpp | 18 +----------------- src/qt/bitcoingui.cpp | 2 +- src/qt/upgradeqt.cpp | 16 +--------------- 6 files changed, 41 insertions(+), 52 deletions(-) diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index 64568da7ac..a1c6253338 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -48,14 +48,13 @@ void Upgrade::ScheduledUpdateCheck() bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dialog, bool snapshotrequest) { // If testnet skip this || If the user changes this to disable while wallet running just drop out of here now. (need a way to remove items from scheduler) - if (fTestNet || GetBoolArg("-disableupdatecheck", false)) + if (fTestNet || (GetBoolArg("-disableupdatecheck", false) && !snapshotrequest)) return false; Http VersionPull; std::string GithubResponse = ""; std::string VersionResponse = ""; - std::string UpdateCheckType = snapshotrequest ? "Snapshot Request" : "Update Checker"; // We receive the response and it's in a json reply UniValue Response(UniValue::VOBJ); @@ -594,3 +593,32 @@ bool Upgrade::SyncFromZero() { return CleanupBlockchainData(); } + +std::string Upgrade::BlockchainCleanupInstructions() +{ + std::stringstream stream; + + // Little more work then needed but we should be translation friendly imo + stream << _("Datadir: "); + stream << GetDataDir().string(); + stream << "\r\n\r\n"; + stream << _("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet."); + stream << "\r\n"; + stream << _("Failure to do so will result in undefined behaviour or failure to start wallet."); + stream << "\r\n\r\n"; + stream << _("You will need to delete the following."); + stream << "\r\n\r\n"; + stream << _("Files:"); + stream << "\r\n"; + stream << "blk000*.dat"; + stream << "\r\n\r\n"; + stream << _("Directories:"); + stream << "\r\n"; + stream << "txleveldb"; + stream << "\r\n"; + stream << "accrual"; + + const std::string& output = stream.str(); + + return output; +} diff --git a/src/gridcoin/upgrade.h b/src/gridcoin/upgrade.h index 16ba8bb625..3ab72e8223 100644 --- a/src/gridcoin/upgrade.h +++ b/src/gridcoin/upgrade.h @@ -97,6 +97,13 @@ class Upgrade //! \returns Bool on the success of blockchain cleanup //! static bool SyncFromZero(); + + //! + //! \brief Small function to return manual erase of blockchain data in event of a syncfromzero clean up of blockchain data + //! + //! \returns String containing manual erase instructions of blockchain data + //! + static std::string BlockchainCleanupInstructions(); }; //! diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index c2eb0ba8d6..2868e90cf8 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -164,23 +164,7 @@ bool AppInit(int argc, char* argv[]) { LogPrintf("Syncfromzero: Failed to clean up blockchain data"); - std::string inftext = ""; - // Little more work then needed but we should be translation friendly imo - inftext.append(_("Sync from zero: Blockchain data removal was a Failure")); - inftext.append("\r\n\r\n"); - inftext.append(_("Datadir: ")); - inftext.append(GetDataDir().string()); - inftext.append("\r\n\r\n"); - inftext.append(_("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet.")); - inftext.append("\r\n"); - inftext.append(_("Failure to do so will result in undefined behaviour or failure to start wallet.")); - inftext.append("\r\n\r\n"); - inftext.append(_("You will need to delete the following.")); - inftext.append("\r\n\r\n"); - inftext.append(_("Files:")); - inftext.append("\r\nblk000*.dat\r\n\r\n"); - inftext.append(_("Directories:")); - inftext.append("\r\ntxleveldb\r\naccrual\r\n"); + std::string inftext = syncfromzero.BlockchainCleanupInstructions(); fprintf(stderr, "%s", inftext.c_str()); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 502ecbed81..b2d81a40f0 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -396,23 +396,7 @@ int main(int argc, char *argv[]) { LogPrintf("Syncfromzero: Failed to clean up blockchain data"); - std::string inftext = ""; - // Little more work then needed but we should be translation friendly imo - inftext.append(_("Sync from zero: Blockchain data removal was a Failure")); - inftext.append("\r\n\r\n"); - inftext.append(_("Datadir: ")); - inftext.append(GetDataDir().string()); - inftext.append("\r\n\r\n"); - inftext.append(_("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet.")); - inftext.append("\r\n"); - inftext.append(_("Failure to do so will result in undefined behaviour or failure to start wallet.")); - inftext.append("\r\n\r\n"); - inftext.append(_("You will need to delete the following.")); - inftext.append("\r\n\r\n"); - inftext.append(_("Files:")); - inftext.append("\r\nblk000*.dat\r\n\r\n"); - inftext.append(_("Directories:")); - inftext.append("\r\ntxleveldb\r\naccrual\r\n"); + std::string inftext = syncfromzero.BlockchainCleanupInstructions(); ThreadSafeMessageBox(inftext, _("Gridcoin"), CClientUIInterface::OK | CClientUIInterface::MODAL); QMessageBox::critical(nullptr, PACKAGE_NAME, QString::fromStdString(inftext)); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d2fab8ef2b..8b7e4f73bc 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1053,7 +1053,7 @@ void BitcoinGUI::syncfromzeroClicked() Msg.setIcon(QMessageBox::Question); Msg.setText(tr("Do you want to delete blockchain data and sync from zero?")); - Msg.setInformativeText(tr("Warning: After the blockchain data is deleted the wallet will shutdown and when restarted will begin syncing from zero. After restart you may continue syncing from zero or use the snapshot download to download the latest snapshot. Your balance will temporarily show as 0 GRC while syncing.")); + Msg.setInformativeText(tr("Warning: After the blockchain data is deleted the wallet will shutdown and when restarted will begin syncing from zero. Your balance will temporarily show as 0 GRC while syncing.")); Msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); Msg.setDefaultButton(QMessageBox::No); diff --git a/src/qt/upgradeqt.cpp b/src/qt/upgradeqt.cpp index f9fe30972d..d298056e6b 100644 --- a/src/qt/upgradeqt.cpp +++ b/src/qt/upgradeqt.cpp @@ -373,21 +373,7 @@ bool UpgradeQt::SyncFromZero(QApplication& SyncfromzeroApp) else { - std::string inftext = ""; - // Little more work then needed but we should be translation friendly imo - inftext.append(_("Datadir: ")); - inftext.append(GetDataDir().string()); - inftext.append("\r\n\r\n"); - inftext.append(_("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet.")); - inftext.append("\r\n"); - inftext.append(_("Failure to do so will result in undefined behaviour or failure to start wallet.")); - inftext.append("\r\n\r\n"); - inftext.append(_("You will need to delete the following.")); - inftext.append("\r\n\r\n"); - inftext.append(_("Files:")); - inftext.append("\r\nblk000*.dat\r\n\r\n"); - inftext.append(_("Directories:")); - inftext.append("\r\ntxleveldb\r\naccrual"); + std::string inftext = syncfromzero.BlockchainCleanupInstructions(); ErrorMsg(_("Sync from zero: Blockchain data removal was a Failure"), inftext); } From bf5451cc9bacf38fdd299136f8dda3278e22d0f9 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 8 Apr 2021 03:53:43 -0500 Subject: [PATCH 012/280] Normalize GUI button styles This updates the styles for the GUI buttons to match the proposed GUI design for the light and dark themes. It fixes the awful button style on Windows and normalizes the button appearance between platforms. --- src/qt/res/stylesheets/dark_stylesheet.qss | 53 ++++++++++++++++----- src/qt/res/stylesheets/light_stylesheet.qss | 44 +++++++++++++---- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 9a2a6a8194..6e78865174 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -279,22 +279,49 @@ QWidget{ color: white; } -QPushButton{ - background-color: rgb(64,68,82); -} - -QPushButton:selected{ - background-color: rgb(65,0,127); - color: white; -} - -QPushButton:hover{ - background-color: rgb(65,0,127); - color: white; +QPushButton { + background: qlineargradient( + x1: 0, + y1: 0, + x2: 0, + y2: 1, + stop: 0 rgb(44, 160, 247), + stop: 1 rgb(26, 145, 235)); + border: 0.065em solid rgb(23, 137, 223); + border-radius: 0.26em; + padding: 0.2em 1.25em; + color: rgb(225, 241, 253); +} + +QPushButton:hover, +QPushButton:focus { + background: qlineargradient( + x1: 0, + y1: 0, + x2: 0, + y2: 1, + stop: 0 rgb(63, 169, 248), + stop: 1 rgb(26, 145, 235)); + border-color: rgb(21, 126, 205); + color: rgb(255, 255, 255); +} + +QPushButton:pressed { + background: qlineargradient( + x1: 0, + y1: 0, + x2: 0, + y2: 1, + stop: 0 rgb(26, 145, 235), + stop: 1 rgb(44, 160, 247)); + border-color: rgb(21, 126, 205); + color: rgb(207, 233, 252); } QPushButton:disabled { - color: rgb(160, 160, 160); + background: rgb(68, 109, 139); + border-color: rgb(38, 124, 188); + color: rgb(211, 232, 248); } QTreeWidget{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 6fe6144b9c..178cc6e9dd 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -263,21 +263,45 @@ QWidget{ color: black; } -QPushButton{ - background-color: rgb(230,230,230); -} - -QPushButton:selected{ - background-color: rgb(65,0,127); - color: white; +QPushButton { + background: qlineargradient( + x1: 0, + y1: 0, + x2: 0, + y2: 1, + stop: 0 rgb(255, 255, 255), + stop: 1 rgb(244, 245, 249)); + border: 0.065em solid rgb(194, 199, 205); + border-radius: 0.26em; + padding: 0.2em 1.25em; + color: rgb(97, 101, 118); +} + +QPushButton:hover { + background: qlineargradient( + x1: 0, + y1: 0, + x2: 0, + y2: 1, + stop: 0 rgb(255, 255, 255), + stop: 1 rgb(244, 236, 255)); + border-color: rgb(120, 20, 255); + color: rgb(88, 92, 107); } -QPushButton:hover{ - background-color: rgb(65,0,127); - color: white; +QPushButton:pressed { + background: qlineargradient( + x1: 0, + y1: 0, + x2: 0, + y2: 1, + stop: 0 rgb(244, 245, 249), + stop: 1 rgb(255, 255, 255)); + border-color: rgb(120, 20, 255); } QPushButton:disabled { + background: rgb(240, 240, 240); color: rgb(140, 140, 140); } From 619ecc54aacde749e4f39a08600a80b2c978cc52 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 8 Apr 2021 03:53:47 -0500 Subject: [PATCH 013/280] Normalize GUI input control styles This updates the styles for the GUI input controls to match the proposed GUI design for the light and dark themes. In doing so, this removes the appearance differences between different types of controls and improves legibility and aesthetics. --- src/Makefile.qt.include | 4 +- src/qt/bitcoin.qrc | 1 + src/qt/res/icons/icons_dark/chevron_up.svg | 1 + src/qt/res/icons/icons_light/chevron_up.svg | 1 + src/qt/res/stylesheets/dark_stylesheet.qss | 194 +++++++++-------- src/qt/res/stylesheets/light_stylesheet.qss | 218 ++++++++++++-------- 6 files changed, 239 insertions(+), 180 deletions(-) create mode 100644 src/qt/res/icons/icons_dark/chevron_up.svg create mode 100644 src/qt/res/icons/icons_light/chevron_up.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index d2974a1f10..117f701c2d 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -368,7 +368,9 @@ RES_ICONS = \ qt/res/icons/icons_light/transactions.svg \ qt/res/icons/icons_native/transactions.svg \ qt/res/icons/icons_light/chevron_down.svg \ - qt/res/icons/icons_dark/chevron_down.svg + qt/res/icons/icons_light/chevron_up.svg \ + qt/res/icons/icons_dark/chevron_down.svg \ + qt/res/icons/icons_dark/chevron_up.svg RES_IMAGES = \ qt/res/images/about.svg \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 0fc9146106..9db02b2518 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -80,6 +80,7 @@ res/icons/tx_por_ss_sent.svg res/icons/message.svg res/icons/icons_light/chevron_down.svg + res/icons/icons_light/chevron_up.svg res/icons/icons_dark/chevron_down.svg diff --git a/src/qt/res/icons/icons_dark/chevron_up.svg b/src/qt/res/icons/icons_dark/chevron_up.svg new file mode 100644 index 0000000000..1cd7479b94 --- /dev/null +++ b/src/qt/res/icons/icons_dark/chevron_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/chevron_up.svg b/src/qt/res/icons/icons_light/chevron_up.svg new file mode 100644 index 0000000000..e37a7d89e5 --- /dev/null +++ b/src/qt/res/icons/icons_light/chevron_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 6e78865174..203f1ffff5 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -12,8 +12,8 @@ QMainWindow { font-family: "Inter"; } -BitcoinAmountField{ - background-color: rgb(49,54,59); +QWidget { + color: white; } /* HLine */ @@ -26,21 +26,23 @@ QFrame[frameShape="5"] { border-left: 0.065em solid rgb(67, 74, 80); } -QToolButton { +QToolBar#toolbar QToolButton { color: white; background-color: rgb(49,54,59); + border: none; + border-radius: 0; } -QToolButton:hover { +QToolBar#toolbar QToolButton:hover { color:white; background-color: rgb(65,0,127); } -QToolButton:checked { +QToolBar#toolbar QToolButton:checked { background-color: rgb(64,68,82); } -QToolButton:checked:hover { +QToolBar#toolbar QToolButton:checked:hover { background-color: rgb(65,0,127); } @@ -196,42 +198,68 @@ QHeaderView::section:hover { color:white; } -QComboBox { - background-color:rgb(35,38,41); - color: white; +QComboBox, +QDateTimeEdit, +QDoubleSpinBox, +QLineEdit, +QPlainTextEdit, +QSpinBox, +QTextEdit, +QToolButton { + background: rgb(24, 34, 44); + border: 0.065em solid rgb(58, 70, 94); + padding: 0.25em 0.5em; + border-radius: 0.26em; + color: rgb(195, 199, 201); + selection-background-color: rgb(26, 145, 235); selection-color: white; - selection-background-color: rgb(65, 0, 127); - border: 0.065em solid rgb(20, 20, 20); - padding: 0.065em 0em 0.065em 0.26em; } -QComboBox::drop-down { - background-color: transparent; - border: none; +QComboBox:hover, +QComboBox:focus, +QDateTimeEdit:focus, +QDoubleSpinBox:focus, +QDoubleSpinBox::up-button:hover, +QDoubleSpinBox::down-button:hover, +QLineEdit:focus, +QPlainTextEdit:focus, +QSpinBox, +QSpinBox::up-button:hover, +QSpinBox::down-button:hover, +QTextEdit:focus, +QToolButton:hover, +QToolButton:focus, +QToolButton:selected { + border-color: rgb(21, 126, 205); + color: rgb(195, 199, 201); } -QComboBox::down-arrow { - image: url(:/icons/dark_chevron_down); +QComboBox:disabled, +QDateTimeEdit:disabled, +QDoubleSpinBox:disabled, +QLineEdit:disabled, +QPlainTextEdit:disabled, +QSpinBox:disabled, +QPlainTextEdit:disabled, +QToolButton:disabled { + background-color: rgb(42, 54, 65); + color: rgb(174, 180, 182); } -QComboBox:hover, -QComboBox:selected { - border-color: rgb(120, 20, 255); -} - -QComboBox:selected { - background-color: rgb(65, 0, 127); +QComboBox::drop-down, +QDateTimeEdit::drop-down { + background-color: transparent; + border: none; } -QComboBox:disabled { - background-color: rgb(47, 52, 56); - border-color: rgb(30, 40, 45); - color: rgb(160, 160, 160); +QComboBox::down-arrow, +QDateTimeEdit::down-arrow { + image: url(:/icons/dark_chevron_down); } QComboBox QAbstractItemView { - background-color:rgb(35,38,41); - color: white; + selection-background-color: rgb(26, 145, 235); + selection-color: white; } QAbstractItemView::item:selected { @@ -239,31 +267,19 @@ QAbstractItemView::item:selected { color: white; } -QAbstractItemView::item:checked { - background-color:rgb(65,0,127); - color: white; -} - -QLineEdit { - background-color:rgb(35,38,41); - color: white; - selection-color: white; - selection-background-color:rgb(65,0,127); -} - -QDateTimeEdit { - background-color:rgb(35,38,41); +QAbstractItemView::item:checked, +QAbstractItemView::item:hover, +QAbstractItemView::item:selected { + background-color: rgb(33, 44, 58); color: white; } QDateTimeEdit QAbstractItemView { background-color:rgb(49,54,59); - color: white; } QDateTimeEdit QAbstractItemView::item:checked { background-color:rgb(65,0,127); - color: white; } QDateTimeEdit QAbstractItemView::item:selected { @@ -275,18 +291,8 @@ QDialog{ background-color: rgb(49,54,59); } -QWidget{ - color: white; -} - QPushButton { - background: qlineargradient( - x1: 0, - y1: 0, - x2: 0, - y2: 1, - stop: 0 rgb(44, 160, 247), - stop: 1 rgb(26, 145, 235)); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(44, 160, 247), stop: 1 rgb(26, 145, 235)); border: 0.065em solid rgb(23, 137, 223); border-radius: 0.26em; padding: 0.2em 1.25em; @@ -295,25 +301,13 @@ QPushButton { QPushButton:hover, QPushButton:focus { - background: qlineargradient( - x1: 0, - y1: 0, - x2: 0, - y2: 1, - stop: 0 rgb(63, 169, 248), - stop: 1 rgb(26, 145, 235)); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(63, 169, 248), stop: 1 rgb(26, 145, 235)); border-color: rgb(21, 126, 205); color: rgb(255, 255, 255); } QPushButton:pressed { - background: qlineargradient( - x1: 0, - y1: 0, - x2: 0, - y2: 1, - stop: 0 rgb(26, 145, 235), - stop: 1 rgb(44, 160, 247)); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(26, 145, 235), stop: 1 rgb(44, 160, 247)); border-color: rgb(21, 126, 205); color: rgb(207, 233, 252); } @@ -350,8 +344,44 @@ QListView::item:checked { color:white; } -QDoubleSpinBox{ - background-color: rgb(35,38,41); +QToolButton { + padding: 0.25em; +} + +QDateEdit::up-button, +QDateEdit::down-button, +QDoubleSpinBox::up-button, +QDoubleSpinBox::down-button, +QSpinBox::up-button, +QSpinBox::down-button { + width: 1.5em; + height: 0.915em; + border-image: none; + border-left: 0.065em solid rgb(58, 70, 94); +} + +QDateEdit::up-button, +QDoubleSpinBox::up-button, +QSpinBox::up-button { + background: transparent; +} + +QDateEdit::down-button, +QDoubleSpinBox::down-button, +QSpinBox::down-button { + border-bottom-right-radius: 0.26em; +} + +QDateEdit::up-arrow, +QDoubleSpinBox::up-arrow, +QSpinBox::up-arrow { + image: url(:/icons/light_chevron_up); +} + +QDateEdit::down-arrow, +QDoubleSpinBox::down-arrow, +QSpinBox::down-arrow { + image: url(:/icons/light_chevron_down); } QTabWidget::tab-bar { @@ -392,20 +422,12 @@ QTabBar::tab:!selected:hover { background-color: rgb(58, 64, 69); } -QTextEdit { - background-color: rgb(35,38,41); -} - /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; font-size: 10pt; } -QPlainTextEdit{ - background-color: rgb(35,38,41); -} - QGroupBox{ background-color: rgb(49,54,59); } @@ -415,16 +437,6 @@ QListWidget{ background-color:rgb(49,54,59); } -QRadioButton{ - color: white; - background-color:rgb(49,54,59); -} - -QCheckBox{ - color: white; - background-color:rgb(49,54,59); -} - /* Main Window*/ #toolbar { diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 178cc6e9dd..600a721bfc 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -11,25 +11,27 @@ QMainWindow { font-family: "Inter"; } -BitcoinAmountField{ - background-color: rgb(240,240,240); +QWidget { + color: rgb(88, 92, 107); } -QToolButton { +QToolBar#toolbar QToolButton { color: black; - background-color: rgb(240,240,240); + background-color: transparent; + border: none; + border-radius: 0; } -QToolButton:hover { +QToolBar#toolbar QToolButton:hover { color:white; background-color: rgb(65,0,127); } -QToolButton:checked { +QToolBar#toolbar QToolButton:checked { background-color: rgb(200,200,200); } -QToolButton:checked:hover { +QToolBar#toolbar QToolButton:checked:hover { background-color: rgb(65,0,127); } @@ -182,40 +184,102 @@ QHeaderView::section:hover { color:white; } -QComboBox { - background-color: rgb(230, 225, 230); - color: black; +QComboBox, +QDateTimeEdit, +QDoubleSpinBox, +QLineEdit, +QPlainTextEdit, +QPushButton, +QSpinBox, +QTextEdit, +QToolButton { + background: white; + border: 0.065em solid rgb(194, 199, 205); + padding: 0.25em 0.5em; + border-radius: 0.26em; + color: rgb(88, 92, 107); selection-background-color: rgb(250, 240, 255); - border: 0.065em solid rgb(173, 173, 173); - padding: 0.065em 0em 0.065em 0.26em; + selection-color: black; } -QComboBox::drop-down { - background-color: transparent; - border: none; +QComboBox:hover, +QComboBox:focus, +QDateTimeEdit:focus, +QDoubleSpinBox:focus, +QDoubleSpinBox::up-button:hover, +QDoubleSpinBox::down-button:hover, +QLineEdit:focus, +QPlainTextEdit:focus, +QPushButton:hover, +QPushButton:focus, +QPushButton:selected, +QSpinBox, +QSpinBox::up-button:hover, +QSpinBox::down-button:hover, +QTextEdit:focus, +QToolButton:hover, +QToolButton:focus, +QToolButton:selected { + border-color: rgb(120, 20, 255); + color: rgb(88, 92, 107); } -QComboBox::down-arrow { - image: url(:/icons/light_chevron_down); +QComboBox:disabled, +QDateTimeEdit:disabled, +QDoubleSpinBox:disabled, +QLineEdit:disabled, +QPlainTextEdit:disabled, +QPushButton:disabled, +QSpinBox:disabled, +QTextEdit:disabled, +QToolButton:disabled { + background-color: rgb(240, 240, 240); + color: rgb(140, 140, 140); +} + +QComboBox, +QPushButton, +QDoubleSpinBox::down-button, +QSpinBox::down-button, +QToolButton { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 245, 249)); } QComboBox:hover, -QComboBox:selected { - border-color: rgb(120, 20, 255); +QComboBox:focus, +QPushButton:hover, +QPushButton:focus, +QPushButton:selected, +QToolButton:hover, +QToolButton:focus, +QToolButton:selected { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 236, 255)); } -QComboBox:selected { - background-color: rgb(250, 240, 255); +QComboBox:on, +QPushButton:pressed, +QToolButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(244, 245, 249), stop: 1 rgb(255, 255, 255)); } -QComboBox:disabled { - background-color: rgb(220, 220, 220); - color: rgb(140, 140, 140); +QComboBox::drop-down, +QDateTimeEdit::drop-down { + background-color: transparent; + border: none; +} + +QComboBox::down-arrow, +QDateTimeEdit::down-arrow { + image: url(:/icons/light_chevron_down); +} + +QComboBox:selected { + background-color: rgb(250, 240, 255); } QComboBox QAbstractItemView { background-color:white; - color: black; + color: rgb(88, 92, 107); } QAbstractItemView::item:selected { @@ -223,20 +287,10 @@ QAbstractItemView::item:selected { color: white; } -QAbstractItemView::item:checked { - background-color:rgb(65,0,127); - color: white; -} - -QLineEdit { - background-color:white; - color: black; - selection-color: white; - selection-background-color:rgb(65,0,127); -} - -QDateTimeEdit { - background-color:white; +QAbstractItemView::item:checked, +QAbstractItemView::item:hover, +QAbstractItemView::item:selected { + background: rgb(241, 245, 247); color: black; } @@ -259,50 +313,14 @@ QDialog{ background-color: rgb(240,240,240); } -QWidget{ - color: black; -} - QPushButton { - background: qlineargradient( - x1: 0, - y1: 0, - x2: 0, - y2: 1, - stop: 0 rgb(255, 255, 255), - stop: 1 rgb(244, 245, 249)); - border: 0.065em solid rgb(194, 199, 205); - border-radius: 0.26em; padding: 0.2em 1.25em; color: rgb(97, 101, 118); } -QPushButton:hover { - background: qlineargradient( - x1: 0, - y1: 0, - x2: 0, - y2: 1, - stop: 0 rgb(255, 255, 255), - stop: 1 rgb(244, 236, 255)); - border-color: rgb(120, 20, 255); - color: rgb(88, 92, 107); -} - -QPushButton:pressed { - background: qlineargradient( - x1: 0, - y1: 0, - x2: 0, - y2: 1, - stop: 0 rgb(244, 245, 249), - stop: 1 rgb(255, 255, 255)); - border-color: rgb(120, 20, 255); -} - -QPushButton:disabled { - background: rgb(240, 240, 240); - color: rgb(140, 140, 140); +QToolButton { + padding: 0.25em; + color: rgb(97, 101, 118); } QTreeWidget{ @@ -331,8 +349,40 @@ QListView::item:checked { color:white; } -QDoubleSpinBox{ - background-color: white; +QDateEdit::up-button, +QDateEdit::down-button, +QDoubleSpinBox::up-button, +QDoubleSpinBox::down-button, +QSpinBox::up-button, +QSpinBox::down-button { + width: 1.5em; + height: 1em; + border-image: none; + border-left: 0.065em solid rgb(194, 199, 205); +} + +QDateEdit::up-button, +QDoubleSpinBox::up-button, +QSpinBox::up-button { + background: transparent; +} + +QDateEdit::down-button, +QDoubleSpinBox::down-button, +QSpinBox::down-button { + border-bottom-right-radius: 0.26em; +} + +QDateEdit::up-arrow, +QDoubleSpinBox::up-arrow, +QSpinBox::up-arrow { + image: url(:/icons/light_chevron_up); +} + +QDateEdit::down-arrow, +QDoubleSpinBox::down-arrow, +QSpinBox::down-arrow { + image: url(:/icons/light_chevron_down); } QTabWidget::tab-bar { @@ -372,20 +422,12 @@ QTabBar::tab:!selected:hover { background: rgb(255, 255, 255); } -QTextEdit { - background-color: white; -} - /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; font-size: 10pt; } -QPlainTextEdit{ - background-color: white; -} - QGroupBox{ background-color: rgb(240,240,240); } From 3078813172cca60ddecb8da4afdd04145327238a Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Sat, 10 Apr 2021 23:15:21 -0700 Subject: [PATCH 014/280] name changes, refactoring, msg function to reduce translations --- src/gridcoin/gridcoin.cpp | 2 +- src/gridcoin/upgrade.cpp | 55 +++++++++++++++++++++++---------------- src/gridcoin/upgrade.h | 17 +++++++++--- src/gridcoinresearchd.cpp | 20 +++++++------- src/init.cpp | 2 +- src/init.h | 2 +- src/qt/bitcoin.cpp | 30 ++++++++++----------- src/qt/bitcoingui.cpp | 16 ++++++------ src/qt/bitcoingui.h | 4 +-- src/qt/upgradeqt.cpp | 25 +++++++----------- src/qt/upgradeqt.h | 2 +- 11 files changed, 93 insertions(+), 82 deletions(-) diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index 05e49f3b94..a6bca14367 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -379,7 +379,7 @@ void ScheduleBeaconDBPassivation(CScheduler& scheduler) std::unique_ptr g_UpdateChecker; bool fSnapshotRequest = false; -bool fSyncfromzeroRequest = false; +bool fResetBlockchainRequest = false; // ----------------------------------------------------------------------------- // Functions diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index a1c6253338..b2299f1ecc 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -187,8 +187,8 @@ void Upgrade::SnapshotMain() if (CheckForLatestUpdate(VersionResponse, false, true)) { - std::cout << _("Unable to download a snapshot, as the wallet has detected that a new mandatory version is available for install. The mandatory upgrade must be installed before the snapshot can be downloaded and applied.") << std::endl; - std::cout << _("Latest Version github data response:") << std::endl; + std::cout << this->ResetBlockchainMessages(UpdateAvailable) << std::endl; + std::cout << this->ResetBlockchainMessages(GithubResponse) << std::endl; std::cout << VersionResponse << std::endl; throw std::runtime_error(_("Failed to download snapshot as mandatory client is available for download.")); @@ -589,34 +589,43 @@ void Upgrade::DeleteSnapshot() } } -bool Upgrade::SyncFromZero() +bool Upgrade::ResetBlockchainData() { return CleanupBlockchainData(); } -std::string Upgrade::BlockchainCleanupInstructions() +std::string Upgrade::ResetBlockchainMessages(ResetBlockchainMsg _msg) { std::stringstream stream; - // Little more work then needed but we should be translation friendly imo - stream << _("Datadir: "); - stream << GetDataDir().string(); - stream << "\r\n\r\n"; - stream << _("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet."); - stream << "\r\n"; - stream << _("Failure to do so will result in undefined behaviour or failure to start wallet."); - stream << "\r\n\r\n"; - stream << _("You will need to delete the following."); - stream << "\r\n\r\n"; - stream << _("Files:"); - stream << "\r\n"; - stream << "blk000*.dat"; - stream << "\r\n\r\n"; - stream << _("Directories:"); - stream << "\r\n"; - stream << "txleveldb"; - stream << "\r\n"; - stream << "accrual"; + switch (_msg) { + case CleanUp: + { + stream << _("Datadir: "); + stream << GetDataDir().string(); + stream << "\r\n\r\n"; + stream << _("Due to the failure to delete the blockchain data you will be required to manually delete the data before starting your wallet."); + stream << "\r\n"; + stream << _("Failure to do so will result in undefined behaviour or failure to start wallet."); + stream << "\r\n\r\n"; + stream << _("You will need to delete the following."); + stream << "\r\n\r\n"; + stream << _("Files:"); + stream << "\r\n"; + stream << "blk000*.dat"; + stream << "\r\n\r\n"; + stream << _("Directories:"); + stream << "\r\n"; + stream << "txleveldb"; + stream << "\r\n"; + stream << "accrual"; + + break; + } + + case UpdateAvailable: stream << _("Unable to download a snapshot, as the wallet has detected that a new mandatory version is available for install. The mandatory upgrade must be installed before the snapshot can be downloaded and applied."); break; + case GithubResponse: stream << _("Latest Version github data response:"); break; + } const std::string& output = stream.str(); diff --git a/src/gridcoin/upgrade.h b/src/gridcoin/upgrade.h index 3ab72e8223..8ccb9536a1 100644 --- a/src/gridcoin/upgrade.h +++ b/src/gridcoin/upgrade.h @@ -42,6 +42,15 @@ class Upgrade //! Upgrade(); + //! + //! \brief Enum for determining the type of message to be returned for ResetBlockchainData functions + //! + enum ResetBlockchainMsg { + CleanUp, + UpdateAvailable, + GithubResponse + }; + //! //! \brief Scheduler call to CheckForLatestUpdate //! @@ -96,14 +105,14 @@ class Upgrade //! //! \returns Bool on the success of blockchain cleanup //! - static bool SyncFromZero(); + static bool ResetBlockchainData(); //! - //! \brief Small function to return manual erase of blockchain data in event of a syncfromzero clean up of blockchain data + //! \brief Small function to return translated messages. //! - //! \returns String containing manual erase instructions of blockchain data + //! \returns String containing message. //! - static std::string BlockchainCleanupInstructions(); + static std::string ResetBlockchainMessages(ResetBlockchainMsg _msg); }; //! diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index 2868e90cf8..a317d72efe 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -99,10 +99,10 @@ bool AppInit(int argc, char* argv[]) // Initialize logging as early as possible. InitLogging(); - // Make sure a user does not request snapshotdownload and syncfromzero at same time! - if (mapArgs.count("-snapshotdownload") && mapArgs.count("-syncfromzero")) + // Make sure a user does not request snapshotdownload and resetblockchaindata at same time! + if (mapArgs.count("-snapshotdownload") && mapArgs.count("-resetblockchaindata")) { - fprintf(stderr, "-snapshotdownload and -syncfromzero cannot be used in conjunction"); + fprintf(stderr, "-snapshotdownload and -resetblockchaindata cannot be used in conjunction"); exit(1); } @@ -142,10 +142,10 @@ bool AppInit(int argc, char* argv[]) snapshot.DeleteSnapshot(); } - // Check to see if the user requested to sync from 0 -- We allow sync from zero on testnet, but not a snapshot download. - if (mapArgs.count("-syncfromzero")) + // Check to see if the user requested to reset blockchain data -- We allow reset blockchain data on testnet, but not a snapshot download. + if (mapArgs.count("-resetblockchaindata")) { - GRC::Upgrade syncfromzero; + GRC::Upgrade resetblockchain; // Let's check make sure gridcoin is not already running in the data directory. if (!LockDirectory(GetDataDir(), ".lock", false)) @@ -157,14 +157,14 @@ bool AppInit(int argc, char* argv[]) else { - if (syncfromzero.SyncFromZero()) - LogPrintf("Syncfromzero: Success"); + if (resetblockchain.ResetBlockchainData()) + LogPrintf("ResetBlockchainData: success"); else { - LogPrintf("Syncfromzero: Failed to clean up blockchain data"); + LogPrintf("ResetBlockchainData: failed to clean up blockchain data"); - std::string inftext = syncfromzero.BlockchainCleanupInstructions(); + std::string inftext = resetblockchain.ResetBlockchainMessages(resetblockchain.CleanUp); fprintf(stderr, "%s", inftext.c_str()); diff --git a/src/init.cpp b/src/init.cpp index 1b5ac76630..4b8f6b80fc 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -310,7 +310,7 @@ std::string HelpMessage() " -disableupdatecheck " + _("Optional: Disable update checks by wallet") + "\n" " -updatecheckinterval= " + _("Optional: Check for updates every hours (default: 120, minimum: 1)") + "\n" " -updatecheckurl= " + _("Optional: URL for the update version checks (ex: https://sub.domain.com/location/latest") + "\n" - " -syncfromzero " + _("Sync blockchain from zero. This argument will remove all previous blockchain data") + "\n"; + " -resetblockchaindata " + _("Reset blockchain data. This argument will remove all previous blockchain data") + "\n"; return strUsage; } diff --git a/src/init.h b/src/init.h index 13f9853387..54c6cb6ece 100644 --- a/src/init.h +++ b/src/init.h @@ -25,5 +25,5 @@ std::string VersionMessage(); std::string LogSomething(); extern bool fSnapshotRequest; -extern bool fSyncfromzeroRequest; +extern bool fResetBlockchainRequest; #endif diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index b2d81a40f0..575e5adfeb 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -353,10 +353,10 @@ int main(int argc, char *argv[]) // Do this early as we don't want to bother initializing if we are just calling IPC ipcScanRelay(argc, argv); - // Make sure a user does not request snapshotdownload and syncfromzero at same time! - if (mapArgs.count("-snapshotdownload") && mapArgs.count("-syncfromzero")) + // Make sure a user does not request snapshotdownload and resetblockchaindata at same time! + if (mapArgs.count("-snapshotdownload") && mapArgs.count("-resetblockchaindata")) { - LogPrintf("-snapshotdownload and -syncfromzero cannot be used in conjunction"); + LogPrintf("-snapshotdownload and -resetblockchaindata cannot be used in conjunction"); return EXIT_FAILURE; } @@ -384,19 +384,19 @@ int main(int argc, char *argv[]) snapshot.DeleteSnapshot(); } - // Check to see if the user requested to sync from 0 -- We allow on testnet. - if (mapArgs.count("-syncfromzero")) + // Check to see if the user requested to reset blockchain data -- We allow on testnet. + if (mapArgs.count("-resetblockchaindata")) { - GRC::Upgrade syncfromzero; + GRC::Upgrade resetblockchain; - if (syncfromzero.SyncFromZero()) - LogPrintf("Syncfromzero: Success"); + if (resetblockchain.ResetBlockchainData()) + LogPrintf("ResetBlockchainData: success"); else { - LogPrintf("Syncfromzero: Failed to clean up blockchain data"); + LogPrintf("ResetBlockchainData: failed to clean up blockchain data"); - std::string inftext = syncfromzero.BlockchainCleanupInstructions(); + std::string inftext = resetblockchain.ResetBlockchainMessages(resetblockchain.CleanUp); ThreadSafeMessageBox(inftext, _("Gridcoin"), CClientUIInterface::OK | CClientUIInterface::MODAL); QMessageBox::critical(nullptr, PACKAGE_NAME, QString::fromStdString(inftext)); @@ -450,9 +450,9 @@ int main(int argc, char *argv[]) } // We received a request to remove blockchain data so client user can start to sync from 0 - if (fSyncfromzeroRequest) + if (fResetBlockchainRequest) { - UpgradeQt Syncfromzero; + UpgradeQt resetblockchain; // Release LevelDB file handles on Windows so we can remove the old // blockchain files: @@ -464,11 +464,11 @@ int main(int argc, char *argv[]) // CTxDB().Close(); - if (Syncfromzero.SyncFromZero(app)) - LogPrintf("Syncfromzero: Success!"); + if (resetblockchain.ResetBlockchain(app)) + LogPrintf("ResetBlockchainData: success"); else - LogPrintf("Syncfromzero: Failed!"); + LogPrintf("ResetBlockchainData: failed"); } return EXIT_SUCCESS; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 8b7e4f73bc..383e62103c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -360,8 +360,8 @@ void BitcoinGUI::createActions() openRPCConsoleAction->setToolTip(tr("Open debugging and diagnostic console")); snapshotAction = new QAction(tr("&Snapshot Download"), this); snapshotAction->setToolTip(tr("Download and apply latest snapshot")); - syncfromzeroAction = new QAction(tr("Sync from &Zero"), this); - syncfromzeroAction->setToolTip(tr("Remove blockchain data and start chain from zero")); + resetblockchainAction = new QAction(tr("&Reset blockchain data"), this); + resetblockchainAction->setToolTip(tr("Remove blockchain data and start chain from zero")); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); @@ -377,7 +377,7 @@ void BitcoinGUI::createActions() connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); connect(diagnosticsAction, SIGNAL(triggered()), this, SLOT(diagnosticsClicked())); connect(snapshotAction, SIGNAL(triggered()), this, SLOT(snapshotClicked())); - connect(syncfromzeroAction, SIGNAL(triggered()), this, SLOT(syncfromzeroClicked())); + connect(resetblockchainAction, SIGNAL(triggered()), this, SLOT(resetblockchainClicked())); } void BitcoinGUI::setIcons() @@ -412,7 +412,7 @@ void BitcoinGUI::setIcons() openRPCConsoleAction->setIcon(QPixmap(":/icons/debugwindow")); snapshotAction->setIcon(QPixmap(":/images/gridcoin")); openConfigAction->setIcon(QPixmap(":/icons/edit")); - syncfromzeroAction->setIcon(QPixmap(":/images/gridcoin")); + resetblockchainAction->setIcon(QPixmap(":/images/gridcoin")); } void BitcoinGUI::createMenuBar() @@ -439,7 +439,7 @@ void BitcoinGUI::createMenuBar() } file->addSeparator(); - file->addAction(syncfromzeroAction); + file->addAction(resetblockchainAction); file->addSeparator(); file->addAction(quitAction); @@ -1047,13 +1047,13 @@ void BitcoinGUI::snapshotClicked() } } -void BitcoinGUI::syncfromzeroClicked() +void BitcoinGUI::resetblockchainClicked() { QMessageBox Msg; Msg.setIcon(QMessageBox::Question); Msg.setText(tr("Do you want to delete blockchain data and sync from zero?")); - Msg.setInformativeText(tr("Warning: After the blockchain data is deleted the wallet will shutdown and when restarted will begin syncing from zero. Your balance will temporarily show as 0 GRC while syncing.")); + Msg.setInformativeText(tr("Warning: After the blockchain data is deleted, the wallet will shutdown and when restarted will begin syncing from zero. Your balance will temporarily show as 0 GRC while syncing.")); Msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); Msg.setDefaultButton(QMessageBox::No); @@ -1076,7 +1076,7 @@ void BitcoinGUI::syncfromzeroClicked() else { - fSyncfromzeroRequest = true; + fResetBlockchainRequest = true; qApp->quit(); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 976bd65422..771ba376ea 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -122,7 +122,7 @@ class BitcoinGUI : public QMainWindow QAction *lockWalletAction; QAction *openRPCConsoleAction; QAction *snapshotAction; - QAction *syncfromzeroAction; + QAction *resetblockchainAction; QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; @@ -226,7 +226,7 @@ private slots: void diagnosticsClicked(); void peersClicked(); void snapshotClicked(); - void syncfromzeroClicked(); + void resetblockchainClicked(); #ifndef Q_OS_MAC /** Handle tray icon clicked */ diff --git a/src/qt/upgradeqt.cpp b/src/qt/upgradeqt.cpp index d298056e6b..b2c7be1c87 100644 --- a/src/qt/upgradeqt.cpp +++ b/src/qt/upgradeqt.cpp @@ -34,14 +34,7 @@ bool UpgradeQt::SnapshotMain(QApplication& SnapshotApp) if (UpgradeMain.CheckForLatestUpdate(VersionResponse, false, true)) { - QMessageBox Msg; - - Msg.setIcon(QMessageBox::Critical); - Msg.setText(ToQString(_("Unable to download a snapshot, as the wallet has detected that a new mandatory version is available for install. The mandatory upgrade must be installed before the snapshot can be downloaded and applied."))); - Msg.setInformativeText(ToQString(_("Latest Version github data response:\r\n") + VersionResponse)); - Msg.setStandardButtons(QMessageBox::Ok); - - Msg.exec(); + ErrorMsg(UpgradeMain.ResetBlockchainMessages(Upgrade::UpdateAvailable), UpgradeMain.ResetBlockchainMessages(Upgrade::GithubResponse) + "\r\n" + VersionResponse); return false; } @@ -359,23 +352,23 @@ void UpgradeQt::DeleteSnapshot() } } -bool UpgradeQt::SyncFromZero(QApplication& SyncfromzeroApp) +bool UpgradeQt::ResetBlockchain(QApplication& ResetBlockchainApp) { - SyncfromzeroApp.processEvents(); - SyncfromzeroApp.setWindowIcon(QPixmap(":/images/gridcoin")); + ResetBlockchainApp.processEvents(); + ResetBlockchainApp.setWindowIcon(QPixmap(":/images/gridcoin")); - Upgrade syncfromzero; + Upgrade resetblockchain; - bool fSuccess = syncfromzero.CleanupBlockchainData(); + bool fSuccess = resetblockchain.CleanupBlockchainData(); if (fSuccess) - Msg(_("Sync from zero: Blockchain data removal was a Success"), _("The wallet will now shutdown. Please start your wallet to begin sync from zero"), false); + Msg(_("Reset Blockchain Data: Blockchain data removal was a success"), _("The wallet will now shutdown. Please start your wallet to begin sync from zero"), false); else { - std::string inftext = syncfromzero.BlockchainCleanupInstructions(); + std::string inftext = resetblockchain.ResetBlockchainMessages(Upgrade::CleanUp); - ErrorMsg(_("Sync from zero: Blockchain data removal was a Failure"), inftext); + ErrorMsg(_("Reset Blockchain Data: Blockchain data removal was a failure"), inftext); } return fSuccess; diff --git a/src/qt/upgradeqt.h b/src/qt/upgradeqt.h index 979af75b52..9193f91059 100644 --- a/src/qt/upgradeqt.h +++ b/src/qt/upgradeqt.h @@ -71,7 +71,7 @@ class UpgradeQt //! //! \return Returns success of blockchain data cleanup task. //! - static bool SyncFromZero(QApplication& SyncfromzeroApp); + static bool ResetBlockchain(QApplication& ResetBlockchainApp); }; #endif // UPGRADEQT_H From fd86ddd1d99293922c498e4f007dc34360705e1c Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 11 Apr 2021 11:36:39 -0500 Subject: [PATCH 015/280] Fix GUI macOS and designer font sizes This change enables an increased font size for Windows and Linux only. Because macOS often uses a different reference DPI, a fixed point-size adjustment like we defined before can actually result in smaller text. Instead, we rely on the default font size for macOS builds. This also fixes a side-effect of the global stylesheet font size value which overrides the sizes configured for text in the designer forms. --- src/qt/bitcoingui.cpp | 12 ++++++++++++ src/qt/res/stylesheets/dark_stylesheet.qss | 2 -- src/qt/res/stylesheets/light_stylesheet.qss | 2 -- src/qt/res/stylesheets/native_stylesheet.qss | 2 -- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f852cbb30e..edb8d5207b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -112,6 +112,18 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QFontDatabase::addApplicationFont(":/fonts/inter-bold"); QFontDatabase::addApplicationFont(":/fonts/inter-regular"); QFontDatabase::addApplicationFont(":/fonts/inconsolata-regular"); + +#ifndef Q_OS_MAC + // This slightly enlarges the application's base font size on Windows and + // Linux. MacOS often uses a different reference DPI so this can actually + // cause the rendered text to appear smaller. On Mac, the default size is + // adequate. + // + QFont appFont = qApp->font(); + appFont.setPointSize(10); + qApp->setFont(appFont); +#endif + setWindowTitle(tr("Gridcoin") + " " + tr("Wallet")); #ifndef Q_OS_MAC diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 203f1ffff5..bd001997b1 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -2,7 +2,6 @@ * { font-family: "Inter"; - font-size: 10pt; } @@ -425,7 +424,6 @@ QTabBar::tab:!selected:hover { /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; - font-size: 10pt; } QGroupBox{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 600a721bfc..d56eace429 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -2,7 +2,6 @@ * { font-family: "Inter"; - font-size: 10pt; } QMainWindow { @@ -425,7 +424,6 @@ QTabBar::tab:!selected:hover { /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; - font-size: 10pt; } QGroupBox{ diff --git a/src/qt/res/stylesheets/native_stylesheet.qss b/src/qt/res/stylesheets/native_stylesheet.qss index 19a3a3251e..9eefeba001 100644 --- a/src/qt/res/stylesheets/native_stylesheet.qss +++ b/src/qt/res/stylesheets/native_stylesheet.qss @@ -2,7 +2,6 @@ * { font-family: "Inter"; - font-size: 10pt; } QMainWindow { @@ -30,7 +29,6 @@ QTextEdit { /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; - font-size: 10pt; } /* Main Window*/ From 848bc831075223c571c56a78912de8114687d8d8 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 11 Apr 2021 00:57:30 +0300 Subject: [PATCH 016/280] Move CTransaction methods to header --- src/primitives/transaction.cpp | 18 ------------------ src/primitives/transaction.h | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 3b316473e5..ed49aa3961 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -108,21 +108,3 @@ bool CTransaction::IsNewerThan(const CTransaction& old) const } return fNewer; } - - -const std::vector& CTransaction::GetContracts() const -{ - if (nVersion == 1 && vContracts.empty() && GRC::Contract::Detect(hashBoinc)) { - REF(vContracts).emplace_back(GRC::Contract::Parse(hashBoinc)); - } - - return vContracts; -} - - -std::vector CTransaction::PullContracts() -{ - GetContracts(); // Populate vContracts for legacy transactions. - - return std::move(vContracts); -} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 8de080272f..f14729a9ff 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -317,14 +317,26 @@ class CTransaction //! \return The set of contracts contained in the transaction. Version 1 //! transactions can only store one contract. //! - const std::vector& GetContracts() const; + const std::vector& GetContracts() const + { + if (nVersion == 1 && vContracts.empty() && GRC::Contract::Detect(hashBoinc)) { + REF(vContracts).emplace_back(GRC::Contract::Parse(hashBoinc)); + } + + return vContracts; + } //! //! \brief Move the contracts contained in the transaction. //! //! \return The set of contracts contained in the transaction. //! - std::vector PullContracts(); + std::vector PullContracts() + { + GetContracts(); // Populate vContracts for legacy transactions. + + return std::move(vContracts); + } }; From 1564ed9f6955acf094aa8905c5da146becd64961 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 11 Apr 2021 20:52:06 +0300 Subject: [PATCH 017/280] Move some functions to tx_verify --- src/Makefile.am | 2 ++ src/consensus/tx_verify.cpp | 54 +++++++++++++++++++++++++++++++++++++ src/consensus/tx_verify.h | 33 +++++++++++++++++++++++ src/main.cpp | 18 ------------- src/main.h | 3 --- src/validation.cpp | 31 +-------------------- src/validation.h | 20 ++++---------- src/wallet/wallet.h | 1 + 8 files changed, 96 insertions(+), 66 deletions(-) create mode 100644 src/consensus/tx_verify.cpp create mode 100644 src/consensus/tx_verify.h diff --git a/src/Makefile.am b/src/Makefile.am index b1a81b528e..299b17debb 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -74,6 +74,7 @@ GRIDCOIN_CORE_H = \ compat/endian.h \ consensus/consensus.h \ consensus/params.h \ + consensus/tx_verify.h \ crypter.h \ fs.h \ fwd.h \ @@ -187,6 +188,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ chainparams.cpp \ chainparamsbase.cpp \ checkpoints.cpp \ + consensus/tx_verify.cpp \ crypter.cpp \ fs.cpp \ gridcoin/appcache.cpp \ diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp new file mode 100644 index 0000000000..463e6a9894 --- /dev/null +++ b/src/consensus/tx_verify.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2017-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) +{ + AssertLockHeld(cs_main); + // Time based nLockTime implemented in 0.1.6 + if (tx.nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) + return true; + for (auto const& txin : tx.vin) + if (!txin.IsFinal()) + return false; + return true; +} + +unsigned int GetLegacySigOpCount(const CTransaction& tx) +{ + unsigned int nSigOps = 0; + for (auto const& txin : tx.vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + for (auto const& txout : tx.vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} + +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const MapPrevTx& inputs) +{ + if (tx.IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut& prevout = GetOutputFor(tx.vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); + } + return nSigOps; +} diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h new file mode 100644 index 0000000000..b6b78a3809 --- /dev/null +++ b/src/consensus/tx_verify.h @@ -0,0 +1,33 @@ +// Copyright (c) 2017-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CONSENSUS_TX_VERIFY_H +#define BITCOIN_CONSENSUS_TX_VERIFY_H + +#include "main.h" + +class CTransaction; + +/** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @param[in] tx The transaction to count + @return number of sigops tx's outputs will produce when spent + @see FetchInputs +*/ +unsigned int GetLegacySigOpCount(const CTransaction& tx); + +/** Count ECDSA signature operations in pay-to-script-hash inputs. + @param[in] tx The transaction to count + @param[in] mapInputs Map of previous transactions that have outputs tx is spending + @return maximum number of sigops required to validate tx's inputs + @see FetchInputs +*/ +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const MapPrevTx& inputs); + +/** + * Check if transaction is final and can be included in a block with the + * specified height and time. Consensus critical. + */ +bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); + +#endif // BITCOIN_CONSENSUS_TX_VERIFY_H diff --git a/src/main.cpp b/src/main.cpp index d4f49872be..892282c6b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -532,24 +532,6 @@ bool IsStandardTx(const CTransaction& tx) return true; } -bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) -{ - AssertLockHeld(cs_main); - // Time based nLockTime implemented in 0.1.6 - if (tx.nLockTime == 0) - return true; - if (nBlockHeight == 0) - nBlockHeight = nBestHeight; - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); - if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) - return true; - for (auto const& txin : tx.vin) - if (!txin.IsFinal()) - return false; - return true; -} - int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { diff --git a/src/main.h b/src/main.h index dfeca862bd..9a7983b4af 100644 --- a/src/main.h +++ b/src/main.h @@ -222,9 +222,6 @@ bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew); */ bool IsStandardTx(const CTransaction& tx); -bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); - - /** A transaction with a merkle branch linking it to the block chain. */ class CMerkleTx : public CTransaction diff --git a/src/validation.cpp b/src/validation.cpp index 51f2ee23f9..432a42a386 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -122,7 +122,7 @@ bool CheckTransaction(const CTransaction& tx) return true; } -bool CheckContracts(CTransaction& tx, const MapPrevTx& inputs) +bool CheckContracts(const CTransaction& tx, const MapPrevTx& inputs) { if (tx.nVersion <= 1) { return true; @@ -287,35 +287,6 @@ bool HasMasterKeyInput(const CTransaction& tx, const MapPrevTx& inputs) return false; } -unsigned int GetLegacySigOpCount(const CTransaction& tx) -{ - unsigned int nSigOps = 0; - for (auto const& txin : tx.vin) - { - nSigOps += txin.scriptSig.GetSigOpCount(false); - } - for (auto const& txout : tx.vout) - { - nSigOps += txout.scriptPubKey.GetSigOpCount(false); - } - return nSigOps; -} - -unsigned int GetP2SHSigOpCount(const CTransaction& tx, const MapPrevTx& inputs) -{ - if (tx.IsCoinBase()) - return 0; - - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const CTxOut& prevout = GetOutputFor(tx.vin[i], inputs); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); - } - return nSigOps; -} - bool DisconnectInputs(CTransaction& tx, CTxDB& txdb) { // Relinquish previous transactions' spent pointers diff --git a/src/validation.h b/src/validation.h index d9686773be..09bc51cbf3 100644 --- a/src/validation.h +++ b/src/validation.h @@ -23,6 +23,7 @@ bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout); bool ReadTxFromDisk(CTransaction& tx, COutPoint prevout); bool CheckTransaction(const CTransaction& tx); + //! \brief Check the validity of any contracts contained in the transaction. //! //! \param tx The transaction to check. @@ -33,7 +34,7 @@ bool CheckTransaction(const CTransaction& tx); //! //! \return \c true if all of the contracts in the transaction validate. //! -bool CheckContracts(CTransaction& tx, const MapPrevTx& inputs); +bool CheckContracts(const CTransaction& tx, const MapPrevTx& inputs); /** Check for standard transaction types @param[in] tx Transaction to check @@ -56,21 +57,8 @@ bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs); //! bool HasMasterKeyInput(const CTransaction& tx, const MapPrevTx& inputs); -/** Count ECDSA signature operations the old-fashioned (pre-0.6) way - @param[in] tx The transaction to count - @return number of sigops tx's outputs will produce when spent - @see FetchInputs -*/ -unsigned int GetLegacySigOpCount(const CTransaction& tx); -/** Count ECDSA signature operations in pay-to-script-hash inputs. - @param[in] tx The transaction to count - @param[in] mapInputs Map of previous transactions that have outputs tx is spending - @return maximum number of sigops required to validate tx's inputs - @see FetchInputs -*/ -unsigned int GetP2SHSigOpCount(const CTransaction& tx, const MapPrevTx& inputs); - const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs); + /** Amount of bitcoins coming in to a transaction Note that lightweight clients may not know anything besides the hash of previous transactions, so may not be able to calculate this. @@ -82,6 +70,7 @@ const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs); CAmount GetValueIn(const CTransaction& tx, const MapPrevTx& inputs); bool DisconnectInputs(CTransaction& tx, CTxDB& txdb); + /** Fetch from memory and/or disk. inputsRet keys are transaction hashes. @param[in] tx The transaction to fetch inputs for @param[in] txdb Transaction database @@ -93,6 +82,7 @@ bool DisconnectInputs(CTransaction& tx, CTxDB& txdb); @return Returns true if all inputs are in txdb or mapTestPool */ bool FetchInputs(CTransaction& tx, CTxDB& txdb, const std::map& mapTestPool, bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid); + /** Sanity check previous transactions, then, if all checks succeed, mark them as spent by tx. @param[in] tx The transaction to connect inputs diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 701a33132b..9c965c3f70 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -11,6 +11,7 @@ #include #include "amount.h" +#include "consensus/tx_verify.h" #include "gridcoin/staking/status.h" #include "main.h" #include "key.h" From 19d501cfe5bae80ce6ead0d1963cc8a95c99861c Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 11 Apr 2021 21:32:02 +0300 Subject: [PATCH 018/280] Move IsStandard functions to policy --- src/Makefile.am | 2 + src/main.cpp | 86 +--------------- src/main.h | 6 -- src/policy/policy.cpp | 177 +++++++++++++++++++++++++++++++++ src/policy/policy.h | 26 +++++ src/script.cpp | 21 ---- src/script.h | 1 - src/test/multisig_tests.cpp | 1 + src/test/script_p2sh_tests.cpp | 1 + src/validation.cpp | 63 ------------ src/validation.h | 8 -- 11 files changed, 208 insertions(+), 184 deletions(-) create mode 100644 src/policy/policy.cpp create mode 100644 src/policy/policy.h diff --git a/src/Makefile.am b/src/Makefile.am index 299b17debb..7404ba9111 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -141,6 +141,7 @@ GRIDCOIN_CORE_H = \ net.h \ pbkdf2.h \ policy/fees.h \ + policy/policy.h \ prevector.h \ primitives/transaction.h \ protocol.h \ @@ -233,6 +234,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ noui.cpp \ pbkdf2.cpp \ policy/fees.cpp \ + policy/policy.cpp \ primitives/transaction.cpp \ protocol.cpp \ rpc/blockchain.cpp \ diff --git a/src/main.cpp b/src/main.cpp index 892282c6b7..984c937f03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ #include "gridcoin/tally.h" #include "gridcoin/tx_message.h" #include "policy/fees.h" +#include "policy/policy.h" #include "validation.h" #include @@ -448,91 +449,6 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) // -bool IsStandardTx(const CTransaction& tx) -{ - std::string reason = ""; - if (tx.nVersion > CTransaction::CURRENT_VERSION) - return false; - - // Treat non-final transactions as non-standard to prevent a specific type - // of double-spend attack, as well as DoS attacks. (if the transaction - // can't be mined, the attacker isn't expending resources broadcasting it) - // Basically we don't want to propagate transactions that can't included in - // the next block. - // - // However, IsFinalTx() is confusing... Without arguments, it uses - // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() - // is set to the value of nHeight in the block. However, when IsFinalTx() - // is called within CBlock::AcceptBlock(), the height of the block *being* - // evaluated is what is used. Thus if we want to know if a transaction can - // be part of the *next* block, we need to call IsFinalTx() with one more - // than chainActive.Height(). - // - // Timestamps on the other hand don't get any special treatment, because we - // can't know what timestamp the next block will have, and there aren't - // timestamp applications where it matters. - if (!IsFinalTx(tx, nBestHeight + 1)) { - return false; - } - // nTime has different purpose from nLockTime but can be used in similar attacks - if (tx.nTime > FutureDrift(GetAdjustedTime(), nBestHeight + 1)) { - return false; - } - - // Extremely large transactions with lots of inputs can cost the network - // almost as much to process as they cost the sender in fees, because - // computing signature hashes is O(ninputs*txsize). Limiting transactions - // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = GetSerializeSize(tx, SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) - return false; - - for (auto const& txin : tx.vin) - { - - // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed - // keys. (remember the 520 byte limit on redeemScript size) That works - // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624 - // bytes of scriptSig, which we round off to 1650 bytes for some minor - // future-proofing. That's also enough to spend a 20-of-20 - // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not - // considered standard) - - if (txin.scriptSig.size() > 1650) - return false; - if (!txin.scriptSig.IsPushOnly()) - return false; - if (fEnforceCanonical && !txin.scriptSig.HasCanonicalPushes()) { - return false; - } - } - - unsigned int nDataOut = 0; - txnouttype whichType; - for (auto const& txout : tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType)) - return false; - if (whichType == TX_NULL_DATA) - nDataOut++; - if (txout.nValue == 0) - return false; - if (fEnforceCanonical && !txout.scriptPubKey.HasCanonicalPushes()) { - return false; - } - } - - // not more than one data txout per non-data txout is permitted - // only one data txout is permitted too - if (nDataOut > 1 && nDataOut > tx.vout.size()/2) - { - reason = "multi-op-return"; - return false; - } - - return true; -} - - int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { AssertLockHeld(cs_main); diff --git a/src/main.h b/src/main.h index 9a7983b4af..363d51568f 100644 --- a/src/main.h +++ b/src/main.h @@ -217,12 +217,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew); -/** Check for standard transaction types - @return True if all outputs (scriptPubKeys) use only standard transaction forms -*/ -bool IsStandardTx(const CTransaction& tx); - - /** A transaction with a merkle branch linking it to the block chain. */ class CMerkleTx : public CTransaction { diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp new file mode 100644 index 0000000000..937ed0b5b9 --- /dev/null +++ b/src/policy/policy.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// NOTE: This file is intended to be customised by the end user, and includes only local node policy logic + +#include + +#include "consensus/tx_verify.h" + +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) +{ + std::vector vSolutions; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_MULTISIG) + { + unsigned char m = vSolutions.front()[0]; + unsigned char n = vSolutions.back()[0]; + // Support up to x-of-3 multisig txns as standard + if (n < 1 || n > 3) + return false; + if (m < 1 || m > n) + return false; + } + + return whichType != TX_NONSTANDARD; +} + +bool IsStandardTx(const CTransaction& tx) +{ + std::string reason = ""; + if (tx.nVersion > CTransaction::CURRENT_VERSION) + return false; + + // Treat non-final transactions as non-standard to prevent a specific type + // of double-spend attack, as well as DoS attacks. (if the transaction + // can't be mined, the attacker isn't expending resources broadcasting it) + // Basically we don't want to propagate transactions that can't included in + // the next block. + // + // However, IsFinalTx() is confusing... Without arguments, it uses + // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() + // is set to the value of nHeight in the block. However, when IsFinalTx() + // is called within CBlock::AcceptBlock(), the height of the block *being* + // evaluated is what is used. Thus if we want to know if a transaction can + // be part of the *next* block, we need to call IsFinalTx() with one more + // than chainActive.Height(). + // + // Timestamps on the other hand don't get any special treatment, because we + // can't know what timestamp the next block will have, and there aren't + // timestamp applications where it matters. + if (!IsFinalTx(tx, nBestHeight + 1)) { + return false; + } + // nTime has different purpose from nLockTime but can be used in similar attacks + if (tx.nTime > FutureDrift(GetAdjustedTime(), nBestHeight + 1)) { + return false; + } + + // Extremely large transactions with lots of inputs can cost the network + // almost as much to process as they cost the sender in fees, because + // computing signature hashes is O(ninputs*txsize). Limiting transactions + // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. + unsigned int sz = GetSerializeSize(tx, SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz >= MAX_STANDARD_TX_SIZE) + return false; + + for (auto const& txin : tx.vin) + { + + // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed + // keys. (remember the 520 byte limit on redeemScript size) That works + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624 + // bytes of scriptSig, which we round off to 1650 bytes for some minor + // future-proofing. That's also enough to spend a 20-of-20 + // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not + // considered standard) + + if (txin.scriptSig.size() > 1650) + return false; + if (!txin.scriptSig.IsPushOnly()) + return false; + if (fEnforceCanonical && !txin.scriptSig.HasCanonicalPushes()) { + return false; + } + } + + unsigned int nDataOut = 0; + txnouttype whichType; + for (auto const& txout : tx.vout) { + if (!::IsStandard(txout.scriptPubKey, whichType)) + return false; + if (whichType == TX_NULL_DATA) + nDataOut++; + if (txout.nValue == 0) + return false; + if (fEnforceCanonical && !txout.scriptPubKey.HasCanonicalPushes()) { + return false; + } + } + + // not more than one data txout per non-data txout is permitted + // only one data txout is permitted too + if (nDataOut > 1 && nDataOut > tx.vout.size()/2) + { + reason = "multi-op-return"; + return false; + } + + return true; +} + +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs) +{ + if (tx.IsCoinBase()) + return true; // Coinbases don't use vin normally + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut& prev = GetOutputFor(tx.vin[i], mapInputs); + + std::vector> vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); + if (nArgsExpected < 0) + return false; + + // Transactions with extra stuff in their scriptSigs are + // non-standard. Note that this EvalScript() call will + // be quick, because if there are any operations + // beside "push data" in the scriptSig the + // IsStandard() call returns false + std::vector> stack; + if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, 0)) return false; + + if (whichType == TX_SCRIPTHASH) + { + if (stack.empty()) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + std::vector> vSolutions2; + txnouttype whichType2; + if (!Solver(subscript, whichType2, vSolutions2)) + return false; + if (whichType2 == TX_SCRIPTHASH) + return false; + + int tmpExpected; + tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } + + if (stack.size() != (unsigned int)nArgsExpected) + return false; + } + + return true; +} diff --git a/src/policy/policy.h b/src/policy/policy.h new file mode 100644 index 0000000000..df7efc138e --- /dev/null +++ b/src/policy/policy.h @@ -0,0 +1,26 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POLICY_POLICY_H +#define BITCOIN_POLICY_POLICY_H + +#include "main.h" + +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); + +/** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms +*/ +bool IsStandardTx(const CTransaction& tx); + +/** Check for standard transaction types + @param[in] tx Transaction to check + @param[in] mapInputs Map of previous transactions that have outputs tx is spending + @return True if all inputs (scriptSigs) use only standard transaction forms + @see FetchInputs +*/ +bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs); + +#endif // BITCOIN_POLICY_POLICY_H diff --git a/src/script.cpp b/src/script.cpp index 427ab371e1..65a4eb3aab 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1521,27 +1521,6 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_MULTISIG) - { - unsigned char m = vSolutions.front()[0]; - unsigned char n = vSolutions.back()[0]; - // Support up to x-of-3 multisig txns as standard - if (n < 1 || n > 3) - return false; - if (m < 1 || m > n) - return false; - } - - return whichType != TX_NONSTANDARD; -} - - unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) { unsigned int nResult = 0; diff --git a/src/script.h b/src/script.h index d1b87298d2..2b0043d1f8 100644 --- a/src/script.h +++ b/src/script.h @@ -611,7 +611,6 @@ enum class IsMineResult bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); IsMineResult IsMineInner(const CKeyStore &keystore, const CScript& scriptPubKey); isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey); isminetype IsMine(const CKeyStore& keystore, const CTxDestination &dest); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index fa9956c6f9..0b5302b516 100755 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -11,6 +11,7 @@ #include "keystore.h" #include "main.h" +#include "policy/policy.h" #include "script.h" #include "wallet/wallet.h" #include "wallet/ismine.h" diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp index eafde91426..f59fbf3bb0 100755 --- a/src/test/script_p2sh_tests.cpp +++ b/src/test/script_p2sh_tests.cpp @@ -6,6 +6,7 @@ #include #include "main.h" +#include "policy/policy.h" #include "script.h" #include "validation.h" #include "wallet/wallet.h" diff --git a/src/validation.cpp b/src/validation.cpp index 432a42a386..808878c259 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -204,69 +204,6 @@ CAmount GetValueIn(const CTransaction& tx, const MapPrevTx& inputs) } -// Check transaction inputs, and make sure any -// pay-to-script-hash transactions are evaluating IsStandard scripts -// -// Why bother? To avoid denial-of-service attacks; an attacker -// can submit a standard HASH... OP_EQUAL transaction, -// which will get accepted into blocks. The redemption -// script can be anything; an attacker could use a very -// expensive-to-check-upon-redemption script like: -// DUP CHECKSIG DROP ... repeated 100 times... OP_1 -// -bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs) -{ - if (tx.IsCoinBase()) - return true; // Coinbases don't use vin normally - - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const CTxOut& prev = GetOutputFor(tx.vin[i], mapInputs); - - std::vector> vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) - return false; - int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); - if (nArgsExpected < 0) - return false; - - // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig the - // IsStandard() call returns false - std::vector> stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, 0)) return false; - - if (whichType == TX_SCRIPTHASH) - { - if (stack.empty()) - return false; - CScript subscript(stack.back().begin(), stack.back().end()); - std::vector> vSolutions2; - txnouttype whichType2; - if (!Solver(subscript, whichType2, vSolutions2)) - return false; - if (whichType2 == TX_SCRIPTHASH) - return false; - - int tmpExpected; - tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; - } - - return true; -} - bool HasMasterKeyInput(const CTransaction& tx, const MapPrevTx& inputs) { const CTxDestination master_address = CWallet::MasterAddress().Get(); diff --git a/src/validation.h b/src/validation.h index 09bc51cbf3..9e84d93bd5 100644 --- a/src/validation.h +++ b/src/validation.h @@ -36,14 +36,6 @@ bool CheckTransaction(const CTransaction& tx); //! bool CheckContracts(const CTransaction& tx, const MapPrevTx& inputs); -/** Check for standard transaction types - @param[in] tx Transaction to check - @param[in] mapInputs Map of previous transactions that have outputs tx is spending - @return True if all inputs (scriptSigs) use only standard transaction forms - @see FetchInputs -*/ -bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs); - //! \brief Determine whether a transaction contains an input spent by the //! master key holder. //! From 9d1459a6e09d9ad14f85e0fd5dc737fec196e77e Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 11 Apr 2021 22:35:57 +0300 Subject: [PATCH 019/280] Move fee functions to header --- src/Makefile.am | 1 - src/policy/fees.cpp | 50 --------------------------------------------- src/policy/fees.h | 43 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 53 deletions(-) delete mode 100644 src/policy/fees.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 7404ba9111..10c1476113 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -233,7 +233,6 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ net.cpp \ noui.cpp \ pbkdf2.cpp \ - policy/fees.cpp \ policy/policy.cpp \ primitives/transaction.cpp \ protocol.cpp \ diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp deleted file mode 100644 index 399e5d1d1b..0000000000 --- a/src/policy/fees.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "consensus/consensus.h" -#include "policy/fees.h" - -CAmount GetBaseFee(const CTransaction& tx, enum GetMinFee_mode mode) -{ - // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE - CAmount nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; - - // For block version 11 onwards, which corresponds to CTransaction::CURRENT_VERSION 2, - // a multiplier is used on top of MIN_TX_FEE and MIN_RELAY_TX_FEE - if (tx.nVersion >= 2) - { - nBaseFee *= 10; - } - - return nBaseFee; -} - -CAmount GetMinFee(const CTransaction& tx, unsigned int nBlockSize, enum GetMinFee_mode mode, unsigned int nBytes) -{ - CAmount nBaseFee = GetBaseFee(tx, mode); - - unsigned int nNewBlockSize = nBlockSize + nBytes; - CAmount nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; - - // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 - if (nMinFee < nBaseFee) - { - for (auto const& txout : tx.vout) - if (txout.nValue < CENT) - nMinFee = nBaseFee; - } - - // Raise the price as the block approaches full - if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) - { - if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) - return MAX_MONEY; - nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; -} diff --git a/src/policy/fees.h b/src/policy/fees.h index 96cf94e2f9..d8b4a52dd7 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -16,8 +16,47 @@ enum GetMinFee_mode GMF_SEND, }; -CAmount GetBaseFee(const CTransaction& tx, enum GetMinFee_mode mode=GMF_BLOCK); +inline CAmount GetBaseFee(const CTransaction& tx, enum GetMinFee_mode mode = GMF_BLOCK) +{ + // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE + CAmount nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + + // For block version 11 onwards, which corresponds to CTransaction::CURRENT_VERSION 2, + // a multiplier is used on top of MIN_TX_FEE and MIN_RELAY_TX_FEE + if (tx.nVersion >= 2) + { + nBaseFee *= 10; + } + + return nBaseFee; +} + +inline CAmount GetMinFee(const CTransaction& tx, unsigned int nBlockSize = 1, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0) +{ + CAmount nBaseFee = GetBaseFee(tx, mode); + + unsigned int nNewBlockSize = nBlockSize + nBytes; + CAmount nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; + + // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 + if (nMinFee < nBaseFee) + { + for (auto const& txout : tx.vout) + if (txout.nValue < CENT) + nMinFee = nBaseFee; + } + + // Raise the price as the block approaches full + if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) + { + if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) + return MAX_MONEY; + nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); + } -CAmount GetMinFee(const CTransaction& tx, unsigned int nBlockSize=1, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes = 0); + if (!MoneyRange(nMinFee)) + nMinFee = MAX_MONEY; + return nMinFee; +} #endif // BITCOIN_POLICY_FEES_H From 6b2467db083fe83e9067c803172ef8408e42c3e8 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 10 Apr 2021 23:43:30 -0400 Subject: [PATCH 020/280] Implement GetMinimumConnectionsRequiredForStaking --- src/chainparams.h | 5 +++++ src/miner.cpp | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/chainparams.h b/src/chainparams.h index a13521ef57..926749d38a 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -145,3 +145,8 @@ inline int GetNewbieSnapshotFixHeight() { return fTestNet ? 1480000 : 2197000; } + +inline unsigned int GetMinimumConnectionsRequiredForStaking() +{ + return fTestNet ? 1 : 3; +} diff --git a/src/miner.cpp b/src/miner.cpp index 4ca3f90837..50f15e0ecb 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1156,23 +1156,21 @@ bool IsMiningAllowed(CWallet *pwallet) { LOCK(g_miner_status.lock); g_miner_status.SetReasonNotStaking(GRC::MinerStatus::WALLET_LOCKED); - status=false; + status = false; } if(fDevbuildCripple) { LOCK(g_miner_status.lock); g_miner_status.SetReasonNotStaking(GRC::MinerStatus::TESTNET_ONLY); - status=false; + status = false; } - if (vNodes.empty() || (!fTestNet&& IsInitialBlockDownload()) || - (!fTestNet&& vNodes.size() < 3) - ) + if (vNodes.size() < GetMinimumConnectionsRequiredForStaking() || (!fTestNet && IsInitialBlockDownload())) { LOCK(g_miner_status.lock); g_miner_status.SetReasonNotStaking(GRC::MinerStatus::OFFLINE); - status=false; + status = false; } return status; From db8bf9cdb59fdd5165790e360f6bc8c897770865 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 11 Apr 2021 18:02:34 -0400 Subject: [PATCH 021/280] Update diagnostics to fix multiple bugs and add difficulty test.. --- src/qt/diagnosticsdialog.cpp | 843 ++++++++++++++++++++---------- src/qt/diagnosticsdialog.h | 52 +- src/qt/forms/diagnosticsdialog.ui | 268 +++++----- 3 files changed, 733 insertions(+), 430 deletions(-) diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index fe0c7d2dca..8b30c611ba 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -38,7 +38,7 @@ void DiagnosticsDialog::SetResearcherModel(ResearcherModel *researcherModel) } void DiagnosticsDialog::SetResultLabel(QLabel *label, DiagnosticTestStatus test_status, - DiagnosticResult test_result, QString override_text) + DiagnosticResult test_result, QString override_text, QString tooltip_text) { switch (test_status) { @@ -72,6 +72,9 @@ void DiagnosticsDialog::SetResultLabel(QLabel *label, DiagnosticTestStatus test_ } if (override_text.size()) label->setText(override_text); + label->setToolTip(tooltip_text); + + this->repaint(); } unsigned int DiagnosticsDialog::GetNumberOfTestsPending() @@ -80,7 +83,7 @@ unsigned int DiagnosticsDialog::GetNumberOfTestsPending() unsigned int pending_count = 0; - for (const auto& entry : test_status_map) + for (const auto& entry : m_test_status_map) { if (entry.second == pending) pending_count++; } @@ -92,9 +95,9 @@ DiagnosticsDialog::DiagnosticTestStatus DiagnosticsDialog::GetTestStatus(std::st { LOCK(cs_diagnostictests); - auto entry = test_status_map.find(test_name); + auto entry = m_test_status_map.find(test_name); - if (entry != test_status_map.end()) + if (entry != m_test_status_map.end()) { return entry->second; } @@ -106,31 +109,29 @@ DiagnosticsDialog::DiagnosticTestStatus DiagnosticsDialog::GetTestStatus(std::st unsigned int DiagnosticsDialog::UpdateTestStatus(std::string test_name, QLabel *label, DiagnosticTestStatus test_status, DiagnosticResult test_result, - QString override_text) + QString override_text, QString tooltip_text) { LOCK(cs_diagnostictests); - test_status_map[test_name] = test_status; + m_test_status_map[test_name] = test_status; - SetResultLabel(label, test_status, test_result, override_text); + SetResultLabel(label, test_status, test_result, override_text, tooltip_text); UpdateOverallDiagnosticResult(test_result); - return test_status_map.size(); + return m_test_status_map.size(); } -void DiagnosticsDialog::ResetOverallDiagnosticResult(unsigned int& number_of_tests_in) +void DiagnosticsDialog::ResetOverallDiagnosticResult() { LOCK(cs_diagnostictests); - number_of_tests = number_of_tests_in; - - test_status_map.clear(); + m_test_status_map.clear(); - diagnostic_result_status = pending; + m_overall_diagnostic_result_status = pending; - diagnostic_result = NA; + m_overall_diagnostic_result = NA; } void DiagnosticsDialog::UpdateOverallDiagnosticResult(DiagnosticResult diagnostic_result_in) @@ -138,27 +139,27 @@ void DiagnosticsDialog::UpdateOverallDiagnosticResult(DiagnosticResult diagnosti LOCK(cs_diagnostictests); // Set diagnostic_result_status to completed. This is under lock, so no one can snoop. - diagnostic_result_status = completed; + m_overall_diagnostic_result_status = completed; // If the total number of registered tests is less than the initialized number, then // the overall status is pending by default. - if (test_status_map.size() < number_of_tests) + if (m_test_status_map.size() < m_number_of_tests) { - diagnostic_result_status = pending; + m_overall_diagnostic_result_status = pending; } - diagnostic_result = (DiagnosticResult) std::max(diagnostic_result_in, diagnostic_result); + m_overall_diagnostic_result = (DiagnosticResult) std::max(diagnostic_result_in, m_overall_diagnostic_result); // If diagnostic_result_status is still set to completed, then at least all tests // are registered. Walk through the map of tests one by one and check the status. // The first one encountered that is pending means the overall status is pending. - if (diagnostic_result_status == completed) + if (m_overall_diagnostic_result_status == completed) { - for (const auto& entry : test_status_map) + for (const auto& entry : m_test_status_map) { if (entry.second == pending) { - diagnostic_result_status = pending; + m_overall_diagnostic_result_status = pending; break; } } @@ -168,13 +169,13 @@ void DiagnosticsDialog::UpdateOverallDiagnosticResult(DiagnosticResult diagnosti // Lock should be taken on cs_diagnostictests before calling this function. DiagnosticsDialog::DiagnosticResult DiagnosticsDialog::GetOverallDiagnosticResult() { - return diagnostic_result; + return m_overall_diagnostic_result; } // Lock should be taken on cs_diagnostictests before calling this function. DiagnosticsDialog::DiagnosticTestStatus DiagnosticsDialog::GetOverallDiagnosticStatus() { - return diagnostic_result_status; + return m_overall_diagnostic_result_status; } @@ -182,99 +183,70 @@ void DiagnosticsDialog::DisplayOverallDiagnosticResult() { LOCK(cs_diagnostictests); - SetResultLabel(ui->overallResultResultLabel, GetOverallDiagnosticStatus(), GetOverallDiagnosticResult()); - - this->repaint(); -} + DiagnosticsDialog::DiagnosticTestStatus overall_diagnostic_status = GetOverallDiagnosticStatus(); + DiagnosticsDialog::DiagnosticResult overall_diagnostic_result = GetOverallDiagnosticResult(); -bool DiagnosticsDialog::VerifyBoincPath() -{ - fs::path boincPath = (fs::path) GRC::GetBoincDataDir(); - - if (boincPath.empty()) - boincPath = (fs::path) GetArgument("boincdatadir", ""); - - boincPath = boincPath / "client_state.xml"; - - return fs::exists(boincPath); -} - -bool DiagnosticsDialog::VerifyIsCPIDValid() -{ - return m_researcher_model->hasEligibleProjects(); -} - -bool DiagnosticsDialog::VerifyCPIDIsEligible() -{ - return m_researcher_model->hasActiveBeacon(); -} + QString tooltip; -bool DiagnosticsDialog::VerifyWalletIsSynced() -{ - return !OutOfSyncByAge(); -} + if (overall_diagnostic_result == warning) + { + tooltip = tr("One or more tests have generated a warning status. Wallet operation may be degraded. Please see " + "the individual test tooltips for details and recommended action(s)."); + } + else if (overall_diagnostic_result == failed) + { + tooltip = tr("One or more tests have failed. Proper wallet operation may be significantly degraded or impossible. " + "Please see the individual test tooltips for details and recommended action(s)."); + } + else if (overall_diagnostic_result == passed) + { + tooltip = tr("All tests passed. Your wallet operation is normal."); + } + else + { + tooltip = QString(); + } -bool DiagnosticsDialog::VerifyCPIDHasRAC() -{ - return m_researcher_model->hasRAC(); + SetResultLabel(ui->overallResultResultLabel, overall_diagnostic_status, overall_diagnostic_result, + QString(), tooltip); } -double DiagnosticsDialog::VerifyETTSReasonable() +void DiagnosticsDialog::on_testButton_clicked() { - // We are going to compute the ETTS with ignore_staking_status set to true - // and also use a 960 block diff as the input, which smooths out short - // term fluctuations. The standard 1-1/e confidence (mean) is used. - - double diff = GRC::GetAverageDifficulty(960); - - double result = GRC::GetEstimatedTimetoStake(true, diff); + // Check to see if there is already a test run in progress, and if so return. + // We do not want overlapping test runs. - return result; -} + if (GetNumberOfTestsPending()) + { + LogPrintf("INFO: DiagnosticsDialog::on_testButton_clicked: Tests still in progress from a prior run: %u", + GetNumberOfTestsPending()); -int DiagnosticsDialog::VerifyCountSeedNodes() -{ - LOCK(cs_vNodes); + return; + } - int count = 0; + ResetOverallDiagnosticResult(); + DisplayOverallDiagnosticResult(); - for (auto const& vnodes : vNodes) - if (!vnodes->fInbound) - count++; + // Tests that are common to both investor and researcher mode. - return count; -} + VerifyWalletIsSynced(); -int DiagnosticsDialog::VerifyCountConnections() -{ - LOCK(cs_vNodes); + unsigned int connections = CheckConnectionCount(); - return (int)vNodes.size(); -} + CheckOutboundConnectionCount(); -void DiagnosticsDialog::on_testButton_clicked() -{ - // Check to see if there is already a test run in progress, and if so return. - // We do not want overlapping test runs. + VerifyClock(connections); - if (GetNumberOfTestsPending()) - { - LogPrintf("INFO: DiagnosticsDialog::on_testButton_clicked: Tests still in progress from a prior run: %u", GetNumberOfTestsPending()); - this->repaint(); - return; - } + VerifyTCPPort(); - // This needs to be updated if there are more tests added. - unsigned int number_of_tests = 11; + double diff = CheckDifficulty(); - ResetOverallDiagnosticResult(number_of_tests); - DisplayOverallDiagnosticResult(); + CheckClientVersion(); - // Tests that are N/A if in investor or pool mode. if (m_researcher_model->configuredForInvestorMode() || m_researcher_model->detectedPoolMode()) { // N/A tests for investor/pool mode - UpdateTestStatus("boincPath", ui->boincPathResultLabel, completed, NA); + UpdateTestStatus("boincPath", ui->verifyBoincPathResultLabel, completed, NA); UpdateTestStatus("verifyCPIDValid", ui->verifyCPIDValidResultLabel, completed, NA); UpdateTestStatus("verifyCPIDHasRAC", ui->verifyCPIDHasRACResultLabel, completed, NA); UpdateTestStatus("verifyCPIDIsActive", ui->verifyCPIDIsActiveResultLabel, completed, NA); @@ -282,196 +254,153 @@ void DiagnosticsDialog::on_testButton_clicked() } else { - //BOINC path - UpdateTestStatus("boincPath", ui->boincPathResultLabel, pending, NA); - this->repaint(); + // Tests that are just for researchers - if (VerifyBoincPath()) - { - UpdateTestStatus("boincPath", ui->boincPathResultLabel, completed, passed); - } - else - { - UpdateTestStatus("boincPath", ui->boincPathResultLabel, completed, failed); - } + // BOINC path + VerifyBoincPath(); - //CPID valid - UpdateTestStatus("verifyCPIDValid", ui->verifyCPIDValidResultLabel, pending, NA); - this->repaint(); + // CPID valid + VerifyCPIDValid(); - if (VerifyIsCPIDValid()) - { - UpdateTestStatus("verifyCPIDValid", ui->verifyCPIDValidResultLabel, completed, passed); - } - else - { - UpdateTestStatus("verifyCPIDValid", ui->verifyCPIDValidResultLabel, completed, failed); - } + // CPID has rac + VerifyCPIDHasRAC(); - //CPID has rac - UpdateTestStatus("verifyCPIDHasRAC", ui->verifyCPIDHasRACResultLabel, pending, NA); - this->repaint(); + // cpid is active + VerifyCPIDIsActive(); - if (VerifyCPIDHasRAC()) - { - UpdateTestStatus("verifyCPIDHasRAC", ui->verifyCPIDHasRACResultLabel, completed, passed); - } - else - { - UpdateTestStatus("verifyCPIDHasRAC", ui->verifyCPIDHasRACResultLabel, completed, failed); - } + // check ETTS + CheckETTS(diff); + } - //cpid is active - UpdateTestStatus("verifyCPIDIsActive", ui->verifyCPIDIsActiveResultLabel, pending, NA); - this->repaint(); + DisplayOverallDiagnosticResult(); +} - if (VerifyCPIDIsEligible()) - { - UpdateTestStatus("verifyCPIDIsActive", ui->verifyCPIDIsActiveResultLabel, completed, passed); - } - else - { - UpdateTestStatus("verifyCPIDIsActive", ui->verifyCPIDIsActiveResultLabel, completed, failed); - } +void DiagnosticsDialog::VerifyWalletIsSynced() +{ + UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, pending, NA); - // verify reasonable ETTS - // This is only checked if wallet is a researcher wallet because the purpose is to - // alert the owner that his stake time is too long and therefore there is a chance - // of research rewards loss between stakes due to the 180 day limit. - UpdateTestStatus("checkETTS", ui->checkETTSResultLabel, pending, NA); + if (!OutOfSyncByAge()) + { + UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, passed); + } + else + { + QString tooltip = tr("Your wallet is out of sync with the network. This uses the wallet core sync standard and " + "so may be different than the out-of-sync indicator on the overview screen. This is normal " + "to fail if the wallet is being synced for the first time. It is also normal if the wallet " + "was just started a few minutes ago. In that case, just try the diagnostics again in a few " + "minutes."); - double ETTS = VerifyETTSReasonable() / (24.0 * 60.0 * 60.0); + UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, failed, QString(), tooltip); + } +} - std::string rounded_ETTS; +int DiagnosticsDialog::CheckConnectionCount() +{ + UpdateTestStatus(__func__, ui->checkConnectionCountResultLabel, pending, NA); - //round appropriately for display. - if (ETTS >= 100) - { - rounded_ETTS = RoundToString(ETTS, 0); - } - else if (ETTS >= 10) - { - rounded_ETTS = RoundToString(ETTS, 1); - } - else - { - rounded_ETTS = RoundToString(ETTS, 2); - } + int connections = 0; + int minimum_connections_to_stake = fTestNet ? 1 : 3; + { + LOCK(cs_vNodes); - // ETTS of zero actually means no coins, i.e. infinite. - if (ETTS == 0.0) - { - UpdateTestStatus("checkETTS", ui->checkETTSResultLabel, completed, failed, - tr("Failed: ETTS is infinite. No coins to stake.")); - } - else if (ETTS > 90.0) - { - UpdateTestStatus("checkETTS", ui->checkETTSResultLabel, completed, failed, - tr("Failed: ETTS is infinite. No coins to stake.")); - } - else if (ETTS > 45.0 && ETTS <= 90.0) - { - UpdateTestStatus("checkETTS", ui->checkETTSResultLabel, completed, warning, - tr("Warning: 45 days < ETTS = %1 <= 90 days").arg(QString(rounded_ETTS.c_str()))); - } - else - { - UpdateTestStatus("checkETTS", ui->checkETTSResultLabel, completed, passed, - tr("Passed: ETTS = %1 <= 45 days").arg(QString(rounded_ETTS.c_str()))); - } + connections = vNodes.size(); } - // Tests that are common to both investor and researcher mode. - // wallet synced - UpdateTestStatus("verifyWalletIsSynced", ui->verifyWalletIsSyncedResultLabel, pending, NA); - this->repaint(); + if (connections <= 7 && connections >= minimum_connections_to_stake) + { + QString tooltip = tr("Please check your network and also check the config file and ensure your addnode entries " + "are up-to-date. If you recently started the wallet, you may want to wait another few " + "minutes for connections to build up and test again. Please see " + "https://gridcoin.us/wiki/config-file.html and https://addnodes.cycy.me/."); - if (VerifyWalletIsSynced()) + UpdateTestStatus(__func__, ui->checkConnectionCountResultLabel, completed, warning, + tr("Warning: Count = %1 (Pass = 8+)").arg(QString::number(connections)), tooltip); + } + else if (connections >= 8) { - UpdateTestStatus("verifyWalletIsSynced", ui->verifyWalletIsSyncedResultLabel, completed, passed); + UpdateTestStatus(__func__, ui->checkConnectionCountResultLabel, completed, passed, + tr("Passed: Count = %1").arg(QString::number(connections))); } - else { - UpdateTestStatus("verifyWalletIsSynced", ui->verifyWalletIsSyncedResultLabel, completed, failed); + QString tooltip = tr("You will not be able to stake because you have less than %1 connection(s). Please check " + "your network and also check the config file and ensure your addnode entries are up-to-date. " + "If you recently started the wallet, you may want to wait another few minutes for connections " + "to build up and then test again. Please see https://gridcoin.us/wiki/config-file.html and " + "https://addnodes.cycy.me/.").arg(QString::number(minimum_connections_to_stake)); + + UpdateTestStatus(__func__, ui->checkConnectionCountResultLabel, completed, failed, + tr("Failed: Count = %1").arg(QString::number(connections)), tooltip); } - // clock - UpdateTestStatus("verifyClockResult", ui->verifyClockResultLabel, pending, NA); - this->repaint(); + return connections; +} - VerifyClock(); +void DiagnosticsDialog::CheckOutboundConnectionCount() +{ + UpdateTestStatus(__func__, ui->checkOutboundConnectionCountResultLabel, pending, NA); - // seed nodes - UpdateTestStatus("verifySeedNodes", ui->verifySeedNodesResultLabel, pending, NA); - this->repaint(); + LOCK(cs_vNodes); - unsigned int seed_node_connections = VerifyCountSeedNodes(); + int outbound_connections = 0; - if (seed_node_connections >= 1 && seed_node_connections < 3) + for (const auto& vnodes : vNodes) { - UpdateTestStatus("verifySeedNodes", ui->verifySeedNodesResultLabel, completed, warning, - tr("Warning: Count = %1 (Pass = 3+)").arg(QString::number(seed_node_connections))); + if (!vnodes->fInbound) ++outbound_connections; } - else if(seed_node_connections >= 3) + + if (outbound_connections < 1) { - UpdateTestStatus("verifySeedNodes", ui->verifySeedNodesResultLabel, completed, passed, - tr("Passed: Count = %1").arg(QString::number(seed_node_connections))); + QString tooltip = tr("Your outbound connection count is critically low. Please check your the config file and " + "ensure your addnode entries are up-to-date. If you recently started the wallet, you may " + "want to wait another few minutes for connections to build up and then test again. Please see " + "https://gridcoin.us/wiki/config-file.html and https://addnodes.cycy.me/."); + + UpdateTestStatus(__func__, ui->checkOutboundConnectionCountResultLabel, completed, failed, + tr("Failed: Count = %1").arg(QString::number(outbound_connections)), tooltip); } - else + else if (outbound_connections < 3) { - UpdateTestStatus("verifySeedNodes", ui->verifySeedNodesResultLabel, completed, failed, - tr("Failed: Count = %1").arg(QString::number(seed_node_connections))); - } - - // connection count - UpdateTestStatus("verifyConnections", ui->verifyConnectionsResultLabel, pending, NA); - this->repaint(); + QString tooltip = tr("Your outbound connection count is low. Please check your the config file and " + "ensure your addnode entries are up-to-date. If you recently started the wallet, you may " + "want to wait another few minutes for connections to build up and then test again. Please see " + "https://gridcoin.us/wiki/config-file.html and https://addnodes.cycy.me/."); - unsigned int connections = VerifyCountConnections(); + UpdateTestStatus(__func__, ui->checkOutboundConnectionCountResultLabel, completed, warning, + tr("Warning: Count = %1 (Pass = 3+)").arg(QString::number(outbound_connections)), tooltip); - if (connections <= 7 && connections >= 1) - { - UpdateTestStatus("verifyConnections", ui->verifyConnectionsResultLabel, completed, warning, - tr("Warning: Count = %1 (Pass = 8+)").arg(QString::number(connections))); - } - else if (connections >= 8) - { - UpdateTestStatus("verifyConnections", ui->verifyConnectionsResultLabel, completed, passed, - tr("Passed: Count = %1").arg(QString::number(connections))); } else { - UpdateTestStatus("verifyConnections", ui->verifyConnectionsResultLabel, completed, failed, - tr("Failed: Count = %1").arg(QString::number(connections))); + UpdateTestStatus(__func__, ui->checkOutboundConnectionCountResultLabel, completed, passed, + tr("Passed: Count = %1").arg(QString::number(outbound_connections))); } +} - // tcp port - UpdateTestStatus("verifyTCPPort", ui->verifyTCPPortResultLabel, pending, NA); - this->repaint(); +void DiagnosticsDialog::VerifyClock(unsigned int connections) +{ + UpdateTestStatus(__func__, ui->verifyClockResultLabel, pending, NA); - VerifyTCPPort(); + // This is aligned to the minimum sample size in AddTimeData to compute a nTimeOffset from the sampling of + // connected nodes. If a sufficient sample exists, use the existing time offset from the node rather than + // going through the ntp connection code. The ntp connection code is retained to help people resolve no connection + // situations where their node is so far off they are banned and disconnected from the network. + if (connections >= 5) + { + int64_t time_offset = 0; - // client version - UpdateTestStatus("checkClientVersion", ui->checkClientVersionResultLabel, pending, NA); + { + LOCK(cs_main); - std::string client_message; + time_offset = GetTimeOffset(); + } - if (g_UpdateChecker->CheckForLatestUpdate(client_message, false)) - { - UpdateTestStatus("checkClientVersion", ui->checkClientVersionResultLabel, completed, warning, - tr("Warning: New Client version available:\n %1").arg(QString(client_message.c_str()))); - } - else - { - UpdateTestStatus("checkClientVersion", ui->checkClientVersionResultLabel, completed, passed); - } + clkReportResults(time_offset); - DisplayOverallDiagnosticResult(); -} + return; + } -void DiagnosticsDialog::VerifyClock() -{ QTimer *timerVerifyClock = new QTimer(); // Set up a timeout clock of 10 seconds as a fail-safe. @@ -479,75 +408,69 @@ void DiagnosticsDialog::VerifyClock() timerVerifyClock->start(10 * 1000); QHostInfo NTPHost = QHostInfo::fromName("pool.ntp.org"); - udpSocket = new QUdpSocket(this); + m_udpSocket = new QUdpSocket(this); - connect(udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(clkStateChanged(QAbstractSocket::SocketState))); - connect(udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(clkSocketError())); + connect(m_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, + SLOT(clkStateChanged(QAbstractSocket::SocketState))); + connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, + SLOT(clkSocketError())); - udpSocket->connectToHost(QHostAddress(NTPHost.addresses().first()), 123, QIODevice::ReadWrite); + if (!NTPHost.addresses().empty()) + { + m_udpSocket->connectToHost(QHostAddress(NTPHost.addresses().first()), 123, QIODevice::ReadWrite); + } + else + { + clkSocketError(); + } } void DiagnosticsDialog::clkStateChanged(QAbstractSocket::SocketState state) { if (state == QAbstractSocket::ConnectedState) { - connect(udpSocket, SIGNAL(readyRead()), this, SLOT(clkFinished())); + connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(clkFinished())); char NTPMessage[48] = {0x1b, 0, 0, 0 ,0, 0, 0, 0, 0}; - udpSocket->write(NTPMessage, sizeof(NTPMessage)); + m_udpSocket->write(NTPMessage, sizeof(NTPMessage)); } - - return; } void DiagnosticsDialog::clkSocketError() { - udpSocket->close(); - - // Call clkFinished to handle. - clkFinished(); + m_udpSocket->close(); - return; + clkReportResults(0, true); } void DiagnosticsDialog::clkFinished() { - if (udpSocket->waitForReadyRead(10 * 1000)) + if (m_udpSocket->waitForReadyRead(10 * 1000)) { int64_t start_time = GetAdjustedTime(); // Only allow this loop to run for 5 seconds maximum. - while (udpSocket->hasPendingDatagrams() && GetAdjustedTime() - start_time <= 5) + while (m_udpSocket->hasPendingDatagrams() && GetAdjustedTime() - start_time <= 5) { - QByteArray BufferSocket = udpSocket->readAll(); + QByteArray BufferSocket = m_udpSocket->readAll(); if (BufferSocket.size() == 48) { int nNTPCount = 40; - unsigned long DateTimeIn = uchar(BufferSocket.at(nNTPCount)) + uint32_t DateTimeIn = uchar(BufferSocket.at(nNTPCount)) + (uchar(BufferSocket.at(nNTPCount + 1)) << 8) + (uchar(BufferSocket.at(nNTPCount + 2)) << 16) + (uchar(BufferSocket.at(nNTPCount + 3)) << 24); - long tmit = ntohl((time_t)DateTimeIn); - tmit -= 2208988800U; + time_t tmit = ntohl(DateTimeIn) - 2208988800U; - udpSocket->close(); + m_udpSocket->close(); boost::posix_time::ptime localTime = boost::posix_time::microsec_clock::universal_time(); boost::posix_time::ptime networkTime = boost::posix_time::from_time_t(tmit); boost::posix_time::time_duration timeDiff = networkTime - localTime; - if (timeDiff.minutes() < 3) - { - UpdateTestStatus("verifyClockResult", ui->verifyClockResultLabel, completed, passed); - } - else - { - UpdateTestStatus("verifyClockResult", ui->verifyClockResultLabel, completed, failed); - } - - DisplayOverallDiagnosticResult(); + clkReportResults(timeDiff.total_seconds()); return; } @@ -557,44 +480,392 @@ void DiagnosticsDialog::clkFinished() { // This is needed to "cancel" the timeout timer. Essentially if the test was marked completed via the normal exits // above, then when the timer calls clkFinished again, it will hit this conditional and be a no-op. - if (GetTestStatus("verifyClockResult") != completed) + if (GetTestStatus("VerifyClock") != completed) { - UpdateTestStatus("verifyClockResult", ui->verifyClockResultLabel,completed, warning, - tr("Warning: Cannot connect to NTP server")); - - DisplayOverallDiagnosticResult(); + clkReportResults(0, true); } return; } } +void DiagnosticsDialog::clkReportResults(const int64_t& time_offset, const bool& timeout_during_check) +{ + if (!timeout_during_check) + { + if (abs64(time_offset) < 3 * 60) + { + UpdateTestStatus("VerifyClock", ui->verifyClockResultLabel, completed, passed); + } + else if (abs64(time_offset) < 5 * 60) + { + QString tooltip = tr("You should check your time and time zone settings for your computer."); + + UpdateTestStatus("VerifyClock", ui->verifyClockResultLabel, completed, warning, + tr("Warning: Clock skew is between 3 and 5 minutes. Please check your clock settings."), + tooltip); + } + else + { + QString tooltip = tr("Your clock in your computer is significantly off from UTC or network time and " + "this may seriously degrade the operation of the wallet, including maintaining " + "connection to the network. You should check your time and time zone settings " + "for your computer. A very common problem is the off by one hour caused by a time " + "zone issue or problems with daylight savings time."); + + UpdateTestStatus("VerifyClock", ui->verifyClockResultLabel, completed, failed, + tr("Error: Clock skew is 5 minutes or greater. Please check your clock settings."), + tooltip); + } + } + else + { + QString tooltip = tr("The wallet has less than five connections to the network and is unable to connect " + "to an NTP server to check your computer clock. This is not necessarily a problem. " + "You can wait a few minutes and try the test again."); + + UpdateTestStatus("VerifyClock", ui->verifyClockResultLabel, completed, warning, + tr("Warning: Cannot connect to NTP server"), tooltip); + } + + DisplayOverallDiagnosticResult(); +} + void DiagnosticsDialog::VerifyTCPPort() { - tcpSocket = new QTcpSocket(this); + UpdateTestStatus(__func__, ui->verifyTCPPortResultLabel, pending, NA); + + m_tcpSocket = new QTcpSocket(this); - connect(tcpSocket, SIGNAL(connected()), this, SLOT(TCPFinished())); - connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(TCPFailed(QAbstractSocket::SocketError))); + connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(TCPFinished())); + connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(TCPFailed(QAbstractSocket::SocketError))); - tcpSocket->connectToHost("portquiz.net", GetListenPort()); + m_tcpSocket->connectToHost("portquiz.net", GetListenPort()); } void DiagnosticsDialog::TCPFinished() { - tcpSocket->close(); - UpdateTestStatus("verifyTCPPort", ui->verifyTCPPortResultLabel, completed, passed); + m_tcpSocket->close(); + UpdateTestStatus("VerifyTCPPort", ui->verifyTCPPortResultLabel, completed, passed); DisplayOverallDiagnosticResult(); return; } -void DiagnosticsDialog::TCPFailed(QAbstractSocket::SocketError socket) +void DiagnosticsDialog::TCPFailed(QAbstractSocket::SocketError socket_error) { - UpdateTestStatus("verifyTCPPort", ui->verifyTCPPortResultLabel, completed, warning, - tr("Warning: Port 32749 may be blocked by your firewall")); + DiagnosticResult test_result = warning; + QString tooltip = tr("Outbound communication to TCP port %1 appears to be blocked. ").arg(GetListenPort()); + + switch (socket_error) + { + case QAbstractSocket::SocketError::ConnectionRefusedError: + tooltip += tr("The connection to the port test site was refused. This could be a transient problem with the " + "port test site, but could also be an issue with your firewall. If you are also failing the " + "connection test, your firewall is most likely blocking network communications from the " + "Gridcoin client."); + + break; + + case QAbstractSocket::SocketError::RemoteHostClosedError: + tooltip += tr("The port test site is closed on port. This could be a transient problem with the " + "port test site, but could also be an issue with your firewall. If you are also failing the " + "connection test, your firewall is most likely blocking network communications from the " + "Gridcoin client."); + + break; + + case QAbstractSocket::SocketError::HostNotFoundError: + // This does not include the common text above on purpose. + tooltip = tr("The IP for the port test site is unable to be resolved. This could mean your DNS is not working " + "correctly. The wallet may operate without DNS, but it could be severely degraded, especially if " + "the wallet is new and a database of prior successful connections has not been built up. Please " + "check your computer and ensure name resolution is operating correctly."); + + break; + + case QAbstractSocket::SocketError::SocketAccessError: + case QAbstractSocket::SocketError::SocketResourceError: + case QAbstractSocket::SocketError::SocketTimeoutError: + case QAbstractSocket::SocketError::DatagramTooLargeError: + case QAbstractSocket::SocketError::NetworkError: + case QAbstractSocket::SocketError::AddressInUseError: + case QAbstractSocket::SocketError::SocketAddressNotAvailableError: + case QAbstractSocket::SocketError::UnsupportedSocketOperationError: + case QAbstractSocket::SocketError::UnfinishedSocketOperationError: + case QAbstractSocket::SocketError::OperationError: + test_result = failed; + + // This does not include the common text above on purpose. + tooltip = tr("The network has experienced a low-level error and this probably means your IP address or other " + "network connection parameters are not configured correctly. Please check your network configuration " + "on your computer."); + + break; + + case QAbstractSocket::SocketError::ProxyAuthenticationRequiredError: + case QAbstractSocket::SocketError::ProxyConnectionRefusedError: + case QAbstractSocket::SocketError::ProxyConnectionClosedError: + case QAbstractSocket::SocketError::ProxyConnectionTimeoutError: + case QAbstractSocket::SocketError::ProxyNotFoundError: + case QAbstractSocket::SocketError::ProxyProtocolError: + test_result = failed; + + tooltip += tr("Your network may be using a proxy server to communicate to public IP addresses on the Internet, and " + "the wallet is not configured properly to use it. Please check the proxy settings under Options -> " + "Network -> Connect through SOCKS proxy."); + + break; + + case QAbstractSocket::SocketError::SslHandshakeFailedError: + case QAbstractSocket::SocketError::SslInternalError: + case QAbstractSocket::SocketError::SslInvalidUserDataError: + test_result = failed; + + // This does not include the common text above on purpose. + tooltip = tr("The network is reporting an SSL error. Gridcoin does not use SSL for normal wallet connections, " + "so this error is extremely unusual. Please check your computer's network configuration."); + + break; + + case QAbstractSocket::SocketError::TemporaryError: + case QAbstractSocket::SocketError::UnknownSocketError: + test_result = failed; + + // This does not include the common text above on purpose. + tooltip = tr("The network is reporting an unspecified socket error. If you also are failing the connection test, " + "then please check your computer's network configuration."); + } + + UpdateTestStatus("VerifyTCPPort", ui->verifyTCPPortResultLabel, completed, test_result, + QString(), tooltip); DisplayOverallDiagnosticResult(); return; } + +double DiagnosticsDialog::CheckDifficulty() +{ + UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, pending, NA); + + double diff = 0; + double scale_factor = 1.0; + + { + LOCK(cs_main); + + scale_factor = fTestNet ? 0.1 : 1.0; + + diff = GRC::GetAverageDifficulty(80); + } + + double fail_diff = scale_factor; + double warn_diff = scale_factor * 5.0; + + if (diff < fail_diff) + { + QString override_text = tr("Failed: 80 block difficulty is less than %1. This wallet is almost certainly forked.") + .arg(QString::number(fail_diff, 'f', 1)); + + QString tooltip = tr("Your difficulty is extremely low and your wallet is almost certainly forked. Please ensure " + "you are running the latest version and try removing the blockchain database and resyncing " + "from genesis using the menu option. (Note this will take 2-4 hours.)"); + + UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, completed, failed, + override_text, tooltip); + } + else if (diff < warn_diff) + { + QString override_text = tr("Warning: 80 block difficulty is less than %1. This wallet is probably forked.") + .arg(QString::number(warn_diff, 'f', 1)); + + QString tooltip = tr("Your difficulty is very low and your wallet is probably forked. Please ensure you are " + "running the latest version and try removing the blockchain database and resyncing from " + "genesis using the menu option. (Note this will take 2-4 hours.)"); + + UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, completed, warning, + override_text, tooltip); + } + else + { + QString override_text = tr("Passed: 80 block difficulty is %1.") + .arg(QString::number(diff, 'f', 1)); + + UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, completed, passed, override_text); + } + + return diff; +} + +void DiagnosticsDialog::CheckClientVersion() +{ + UpdateTestStatus(__func__, ui->checkClientVersionResultLabel, pending, NA); + + std::string client_message; + + if (g_UpdateChecker->CheckForLatestUpdate(client_message, false) + && client_message.find("mandatory") != std::string::npos) + { + QString tooltip = tr("There is a new mandatory version available and you should upgrade as soon as possible to " + "ensure your wallet remains in consensus with the network."); + + UpdateTestStatus(__func__, ui->checkClientVersionResultLabel, completed, failed, + tr("Warning: New Client version available:\n %1").arg(QString(client_message.c_str()))); + } + else if (g_UpdateChecker->CheckForLatestUpdate(client_message, false) + && client_message.find("mandatory") == std::string::npos) + { + QString tooltip = tr("There is a new leisure version available and you should upgrade as soon as practical."); + + UpdateTestStatus(__func__, ui->checkClientVersionResultLabel, completed, warning, + tr("Warning: New Client version available:\n %1").arg(QString(client_message.c_str()))); + } + else + { + UpdateTestStatus(__func__, ui->checkClientVersionResultLabel, completed, passed); + } +} + +void DiagnosticsDialog::VerifyBoincPath() +{ + UpdateTestStatus(__func__, ui->verifyBoincPathResultLabel, pending, NA); + + fs::path boincPath = (fs::path) GRC::GetBoincDataDir(); + + if (boincPath.empty()) + boincPath = (fs::path) GetArgument("boincdatadir", ""); + + boincPath = boincPath / "client_state.xml"; + + if (fs::exists(boincPath)) + { + UpdateTestStatus(__func__, ui->verifyBoincPathResultLabel, completed, passed); + } + else + { + QString tooltip = tr("Check that BOINC is installed and that you have the correct path in the config file " + "if you installed it to a nonstandard location."); + + UpdateTestStatus(__func__, ui->verifyBoincPathResultLabel, completed, failed, QString(), tooltip); + } +} + +void DiagnosticsDialog::VerifyCPIDValid() +{ + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, pending, NA); + + if (m_researcher_model->hasEligibleProjects()) + { + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, completed, passed); + } + else + { + QString tooltip = tr("Verify (1) that you have BOINC installed correctly, (2) that you have attached at least one " + "whitelisted project, (3) that you advertised your beacon with the same email as you use for " + "your BOINC project(s), and (4) that the CPID on the overview screen matches the CPID when " + "you login to your BOINC project(s) online."); + + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, completed, failed, QString(), tooltip); + } +} + +void DiagnosticsDialog::VerifyCPIDHasRAC() +{ + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, pending, NA); + + if (m_researcher_model->hasRAC()) + { + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, completed, passed); + } + else + { + QString tooltip = tr("Verify that you have actually completed workunits for the projects you have attached and " + "that you have authorized the export of statistics. Please see " + "https://gridcoin.us/guides/whitelist.htm."); + + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, completed, failed, QString(), tooltip); + } +} + +void DiagnosticsDialog::VerifyCPIDIsActive() +{ + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, pending, NA); + + if (m_researcher_model->hasActiveBeacon()) + { + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, passed); + } + else + { + QString tooltip = tr("Please ensure that you have followed the process to advertise and verify your beacon. " + "You can use the research wizard (the "Beacon" button on the overview screen)."); + + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, failed, QString(), tooltip); + } +} + +// check ETTS +// This is only checked if wallet is a researcher wallet because the purpose is to +// alert the owner that his stake time is too long and therefore there is a chance +// of research rewards loss between stakes due to the 180 day limit. +void DiagnosticsDialog::CheckETTS(const double& diff) +{ + UpdateTestStatus(__func__, ui->checkETTSResultLabel, pending, NA); + + double ETTS = GRC::GetEstimatedTimetoStake(true, diff) / (24.0 * 60.0 * 60.0); + + std::string rounded_ETTS; + + //round appropriately for display. + if (ETTS >= 100) + { + rounded_ETTS = RoundToString(ETTS, 0); + } + else if (ETTS >= 10) + { + rounded_ETTS = RoundToString(ETTS, 1); + } + else + { + rounded_ETTS = RoundToString(ETTS, 2); + } + + // ETTS of zero actually means no coins, i.e. infinite. + if (ETTS == 0.0) + { + QString tooltip = tr("You have no balance and will be unable to retrieve your research rewards when solo mining. " + "You should acquire GRC to stake so you can retrieve your research rewards. Please see " + "https://gridcoin.us/guides/boinc-install.htm."); + + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, failed, + tr("Failed: ETTS is infinite. No coins to stake."), tooltip); + } + else if (ETTS > 90.0) + { + QString tooltip = tr("Your balance is too low given the current network difficulty to stake in a reasonable " + "period of time to retrieve your research rewards when solo mining. You should acquire more " + "GRC to stake more often."); + + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, failed, + tr("Failed: ETTS is > 90 days. It will take a very long time to receive your research rewards."), + tooltip); + } + else if (ETTS > 45.0 && ETTS <= 90.0) + { + QString tooltip = tr("Your balance is low given the current network difficulty to stake in a reasonable " + "period of time to retrieve your research rewards when solo mining. You should consider " + "acquiring more GRC to stake more often."); + + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, warning, + tr("Warning: 45 days < ETTS = %1 <= 90 days").arg(QString(rounded_ETTS.c_str())), + tooltip); + } + else + { + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, passed, + tr("Passed: ETTS = %1 <= 45 days").arg(QString(rounded_ETTS.c_str()))); + } +} + diff --git a/src/qt/diagnosticsdialog.h b/src/qt/diagnosticsdialog.h index 58bbf4fe05..e2c636372e 100644 --- a/src/qt/diagnosticsdialog.h +++ b/src/qt/diagnosticsdialog.h @@ -43,54 +43,52 @@ class DiagnosticsDialog : public QDialog completed }; - //typedef std::atomic DiagnosticResult; - private: Ui::DiagnosticsDialog *ui; void GetData(); - void VerifyClock(); + void VerifyWalletIsSynced(); + int CheckConnectionCount(); + void CheckOutboundConnectionCount(); + void VerifyClock(unsigned int connections); void VerifyTCPPort(); - bool VerifyBoincPath(); - bool VerifyCPIDIsEligible(); - bool VerifyWalletIsSynced(); - bool VerifyIsCPIDValid(); - bool VerifyCPIDHasRAC(); - double VerifyETTSReasonable(); - int VerifyCountSeedNodes(); - int VerifyCountConnections(); - double GetTotalCPIDRAC(std::string cpid); - double GetUserRAC(std::string cpid, int *projects); - std::string KeyValue(std::string key); + double CheckDifficulty(); + void CheckClientVersion(); + void VerifyBoincPath(); + void VerifyCPIDValid(); + void VerifyCPIDHasRAC(); + void VerifyCPIDIsActive(); + void CheckETTS(const double& diff); // Because some of the tests are "spurs", this object is multithreaded CCriticalSection cs_diagnostictests; // Holds the overall result of all diagnostic tests - DiagnosticResult diagnostic_result; + DiagnosticResult m_overall_diagnostic_result; // Holds the status of the overall diagnostic result - DiagnosticTestStatus diagnostic_result_status; + DiagnosticTestStatus m_overall_diagnostic_result_status; - // Holds the number of tests registered. - unsigned int number_of_tests = 0; + // Holds the number of tests to be registered. + // This needs to be updated if the number of tests is changed. + unsigned int m_number_of_tests = 12; // Holds the test status entries - typedef std::unordered_map mDiagnosticTestStatus; - mDiagnosticTestStatus test_status_map; + typedef std::unordered_map DiagnosticTestStatus_map; + DiagnosticTestStatus_map m_test_status_map; ResearcherModel *m_researcher_model; - QUdpSocket *udpSocket; - QTcpSocket *tcpSocket; + QUdpSocket *m_udpSocket; + QTcpSocket *m_tcpSocket; public: void SetResearcherModel(ResearcherModel *researcherModel); unsigned int GetNumberOfTestsPending(); unsigned int UpdateTestStatus(std::string test_name, QLabel *label, DiagnosticTestStatus test_status, DiagnosticResult test_result, - QString override_text = QString()); + QString override_text = QString(), QString tooltip_text = QString()); DiagnosticTestStatus GetTestStatus(std::string test_name); - void ResetOverallDiagnosticResult(unsigned int& number_of_tests); + void ResetOverallDiagnosticResult(); void UpdateOverallDiagnosticResult(DiagnosticResult diagnostic_result_in); DiagnosticResult GetOverallDiagnosticResult(); DiagnosticTestStatus GetOverallDiagnosticStatus(); @@ -98,15 +96,17 @@ class DiagnosticsDialog : public QDialog private: void SetResultLabel(QLabel *label, DiagnosticTestStatus test_status, - DiagnosticResult test_result, QString override_text = QString()); + DiagnosticResult test_result, QString override_text = QString(), + QString tooltip_text = QString()); private slots: void on_testButton_clicked(); void clkFinished(); void clkStateChanged(QAbstractSocket::SocketState state); void clkSocketError(); + void clkReportResults(const int64_t& time_offset, const bool& timeout_during_check = false); void TCPFinished(); - void TCPFailed(QAbstractSocket::SocketError socketError); + void TCPFailed(QAbstractSocket::SocketError socket_error); }; #endif // DIAGNOSTICSDIALOG_H diff --git a/src/qt/forms/diagnosticsdialog.ui b/src/qt/forms/diagnosticsdialog.ui index 7d57d42ccd..d66d70f94e 100644 --- a/src/qt/forms/diagnosticsdialog.ui +++ b/src/qt/forms/diagnosticsdialog.ui @@ -7,7 +7,7 @@ 0 0 866 - 623 + 659 @@ -49,52 +49,43 @@ 8 - - - - - 0 - 0 - - - - - 200 - 0 - - + + - - - - true + Check estimated time to stake - - 10 + + + + + + Verify outbound port works - - - - - 0 - 0 - + + + + + 12 + 75 + true + - - - 0 - 0 - + + Overall Result + + + + - Verify CPID has valid beacon + Check total connections - - + + 0 @@ -118,8 +109,8 @@ - - + + 0 @@ -143,42 +134,21 @@ - - - - - 12 - 75 - true - - - - Overall Result - - - - - + + - Verify CPID has RAC + Verify wallet is synced - - - - - 14 - 75 - true - - + + - Diagnostics + Verify CPID is valid - + @@ -198,8 +168,8 @@ - - + + 0 @@ -218,23 +188,38 @@ true - - 0 - 10 - - + + + + + 0 + 0 + + + + + 200 + 0 + + - Verify BOINC path + + + + true + + + 10 - - + + 0 @@ -258,22 +243,41 @@ - - + + + + + 0 + 0 + + + + + 0 + 0 + + - Verify listen port for full node + Verify CPID has active beacon - + Verify clock + + + + Verify BOINC path + + + - + 0 @@ -292,23 +296,13 @@ true - - 0 - 10 - - - - Verify connections to network - - - - - + + 0 @@ -332,15 +326,8 @@ - - - - Verify CPID is valid - - - - - + + 0 @@ -359,27 +346,55 @@ true + + 0 + 10 - - + + + + + 14 + 75 + true + + - Check client version + Diagnostics - - + + + + + 0 + 0 + + + + + 200 + 0 + + - Verify connections to seeds + + + + true + + + 10 - + 0 @@ -403,15 +418,29 @@ - - + + - Verify wallet is synced + Check outbound connections + + + + + + + Check difficulty + + + + + + + Verify CPID has RAC - + 0 @@ -430,20 +459,23 @@ true + + 0 + 10 - - + + - Check estimated time to stake + Check client version - - + + 0 From 9306bbe4c06b3a94cf096317753849d61a50fafa Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 10 Apr 2021 19:20:45 -0400 Subject: [PATCH 022/280] Clarify comments about SSL socket error --- src/qt/diagnosticsdialog.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index 8b30c611ba..86a174637e 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -618,14 +618,17 @@ void DiagnosticsDialog::TCPFailed(QAbstractSocket::SocketError socket_error) break; + // SSL errors will NOT be triggered unless we implement a test and site to actually do an SSL connection test. This + // is put here for completeness. case QAbstractSocket::SocketError::SslHandshakeFailedError: case QAbstractSocket::SocketError::SslInternalError: case QAbstractSocket::SocketError::SslInvalidUserDataError: test_result = failed; // This does not include the common text above on purpose. - tooltip = tr("The network is reporting an SSL error. Gridcoin does not use SSL for normal wallet connections, " - "so this error is extremely unusual. Please check your computer's network configuration."); + tooltip = tr("The network is reporting an SSL error. If you also failed or got a warning on your clock test, you " + "should check your clock settings, including your time and time zone. If your clock is ok, please " + "check your computer's network configuration."); break; From 8a449073113c09ee9a1f0869ea2b9a7a4621d4d4 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 10 Apr 2021 21:05:13 -0400 Subject: [PATCH 023/280] Implement g_synced_before atomic boolean This implements a global atomic boolean g_synced_before that is set by OutOfSyncByAge() the first time OutOfSyncByAge() is false. It also adjusts the tests to use different alerting standards based on whether g_synced_before is true or false. --- src/main.cpp | 8 +++++- src/qt/diagnosticsdialog.cpp | 49 ++++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 984c937f03..2c27645600 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -87,6 +87,7 @@ int nBestHeight = -1; uint256 hashBestChain; CBlockIndex* pindexBest = NULL; std::atomic g_previous_block_time; +std::atomic_bool g_synced_before; std::atomic g_nTimeBestReceived; CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -944,7 +945,12 @@ bool OutOfSyncByAge() // rules that Bitcoin uses. constexpr int64_t maxAge = 90 * 10; - return GetAdjustedTime() - g_previous_block_time >= maxAge; + bool out_of_sync = (GetAdjustedTime() - g_previous_block_time >= maxAge); + + // g_synced_before is an atomic boolean global that keeps track of whether the wallet was ever in sync in this run. + if (!out_of_sync) g_synced_before = true; + + return out_of_sync; } diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index 86a174637e..d81a321d09 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -19,6 +19,8 @@ #include +extern std::atomic_bool g_synced_before; + DiagnosticsDialog::DiagnosticsDialog(QWidget *parent, ResearcherModel* researcher_model) : QDialog(parent), ui(new Ui::DiagnosticsDialog), @@ -279,19 +281,31 @@ void DiagnosticsDialog::VerifyWalletIsSynced() { UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, pending, NA); - if (!OutOfSyncByAge()) + if (!g_synced_before and OutOfSyncByAge()) { - UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, passed); + QString tooltip = tr("Your wallet is still in initial sync. If this is a sync from the beginning (genesis), the " + "sync process can take from 2 to 4 hours, or longer on a slow computer. If you have synced " + "your wallet before but you just started the wallet up, then wait a few more minutes and " + "retry the diagnostics again."); + + UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, warning, QString(), tooltip); } - else + else if (g_synced_before && OutOfSyncByAge()) { - QString tooltip = tr("Your wallet is out of sync with the network. This uses the wallet core sync standard and " - "so may be different than the out-of-sync indicator on the overview screen. This is normal " - "to fail if the wallet is being synced for the first time. It is also normal if the wallet " - "was just started a few minutes ago. In that case, just try the diagnostics again in a few " - "minutes."); + QString tooltip = tr("Your wallet is out of sync with the network but was in sync before. If this fails there is " + "likely a severe problem that is preventing the wallet from syncing. If the lack of sync " + "is due to network connection issues, you will see failures on the network connection " + "test(s). If the network connections pass, but your wallet fails this test, and continues to " + "fail this test on repeated attempts with a few minutes in between, this could indicate a " + "more serious issue. In that case you should check the debug log to see if it sheds light " + "on the cause for no sync."); UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, failed, QString(), tooltip); + + } + else + { + UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, passed); } } @@ -667,7 +681,22 @@ double DiagnosticsDialog::CheckDifficulty() double fail_diff = scale_factor; double warn_diff = scale_factor * 5.0; - if (diff < fail_diff) + // If g_synced_before is false, the wallet is still in the initial sync process. In that case use the failure + // standard and just warn, with a different explanation. + if (!g_synced_before && OutOfSyncByAge() && diff < fail_diff) + { + QString override_text = tr("Warning: 80 block difficulty is less than %1.") + .arg(QString::number(fail_diff, 'f', 1)); + + QString tooltip = tr("Your difficulty is low but your wallet is still in initial sync. Please recheck it later " + "to see if this passes."); + + UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, completed, warning, + override_text, tooltip); + } + // If the wallet has been in sync in the past in this run, then apply the normal standards, whether the wallet is + // in sync or not right now. + else if (g_synced_before && diff < fail_diff) { QString override_text = tr("Failed: 80 block difficulty is less than %1. This wallet is almost certainly forked.") .arg(QString::number(fail_diff, 'f', 1)); @@ -679,7 +708,7 @@ double DiagnosticsDialog::CheckDifficulty() UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, completed, failed, override_text, tooltip); } - else if (diff < warn_diff) + else if (g_synced_before && diff < warn_diff) { QString override_text = tr("Warning: 80 block difficulty is less than %1. This wallet is probably forked.") .arg(QString::number(warn_diff, 'f', 1)); From acfe0a14e97dd6f513d059192cdd18688fdfae4b Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 11 Apr 2021 12:24:34 -0400 Subject: [PATCH 024/280] Use warning instead of failure for CPID and ETTS checks if g_synced_before is false and not in sync. --- src/qt/diagnosticsdialog.cpp | 125 ++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 40 deletions(-) diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index d81a321d09..d62bbe9c48 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -794,12 +794,23 @@ void DiagnosticsDialog::VerifyCPIDValid() } else { - QString tooltip = tr("Verify (1) that you have BOINC installed correctly, (2) that you have attached at least one " - "whitelisted project, (3) that you advertised your beacon with the same email as you use for " - "your BOINC project(s), and (4) that the CPID on the overview screen matches the CPID when " - "you login to your BOINC project(s) online."); + if (!g_synced_before && OutOfSyncByAge()) + { + QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."); + + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, completed, warning, QString(), tooltip); + } + else + { + QString tooltip = tr("Verify (1) that you have BOINC installed correctly, (2) that you have attached at least " + "one whitelisted project, (3) that you advertised your beacon with the same email as you " + "use for your BOINC project(s), and (4) that the CPID on the overview screen matches the " + "CPID when you login to your BOINC project(s) online."); - UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, completed, failed, QString(), tooltip); + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, completed, failed, QString(), tooltip); + } } } @@ -813,11 +824,22 @@ void DiagnosticsDialog::VerifyCPIDHasRAC() } else { - QString tooltip = tr("Verify that you have actually completed workunits for the projects you have attached and " - "that you have authorized the export of statistics. Please see " - "https://gridcoin.us/guides/whitelist.htm."); + if (!g_synced_before && OutOfSyncByAge()) + { + QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."); + + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, completed, warning, QString(), tooltip); + } + else + { + QString tooltip = tr("Verify that you have actually completed workunits for the projects you have attached and " + "that you have authorized the export of statistics. Please see " + "https://gridcoin.us/guides/whitelist.htm."); - UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, completed, failed, QString(), tooltip); + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, completed, failed, QString(), tooltip); + } } } @@ -831,10 +853,21 @@ void DiagnosticsDialog::VerifyCPIDIsActive() } else { - QString tooltip = tr("Please ensure that you have followed the process to advertise and verify your beacon. " - "You can use the research wizard (the "Beacon" button on the overview screen)."); + if (!g_synced_before && OutOfSyncByAge()) + { + QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."); - UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, failed, QString(), tooltip); + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, warning, QString(), tooltip); + } + else + { + QString tooltip = tr("Please ensure that you have followed the process to advertise and verify your beacon. " + "You can use the research wizard (the beacon button on the overview screen)."); + + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, failed, QString(), tooltip); + } } } @@ -864,40 +897,52 @@ void DiagnosticsDialog::CheckETTS(const double& diff) rounded_ETTS = RoundToString(ETTS, 2); } - // ETTS of zero actually means no coins, i.e. infinite. - if (ETTS == 0.0) + if (!g_synced_before && OutOfSyncByAge()) { - QString tooltip = tr("You have no balance and will be unable to retrieve your research rewards when solo mining. " - "You should acquire GRC to stake so you can retrieve your research rewards. Please see " - "https://gridcoin.us/guides/boinc-install.htm."); + QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " + "wait for the wallet to sync and retest. If there are other failures preventing the " + "wallet from syncing, please correct those items and retest to see if this test passes."); - UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, failed, - tr("Failed: ETTS is infinite. No coins to stake."), tooltip); + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, warning, QString(), tooltip); } - else if (ETTS > 90.0) + else { - QString tooltip = tr("Your balance is too low given the current network difficulty to stake in a reasonable " - "period of time to retrieve your research rewards when solo mining. You should acquire more " - "GRC to stake more often."); + // ETTS of zero actually means no coins, i.e. infinite. + if (ETTS == 0.0) + { + QString tooltip = tr("You have no balance and will be unable to retrieve your research rewards when solo " + "mining. You should acquire GRC to stake so you can retrieve your research rewards. " + "Please see https://gridcoin.us/guides/boinc-install.htm."); - UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, failed, - tr("Failed: ETTS is > 90 days. It will take a very long time to receive your research rewards."), - tooltip); - } - else if (ETTS > 45.0 && ETTS <= 90.0) - { - QString tooltip = tr("Your balance is low given the current network difficulty to stake in a reasonable " - "period of time to retrieve your research rewards when solo mining. You should consider " - "acquiring more GRC to stake more often."); + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, failed, + tr("Failed: ETTS is infinite. No coins to stake."), tooltip); + } + else if (ETTS > 90.0) + { + QString tooltip = tr("Your balance is too low given the current network difficulty to stake in a reasonable " + "period of time to retrieve your research rewards when solo mining. You should acquire " + "more GRC to stake more often."); - UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, warning, - tr("Warning: 45 days < ETTS = %1 <= 90 days").arg(QString(rounded_ETTS.c_str())), - tooltip); - } - else - { - UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, passed, - tr("Passed: ETTS = %1 <= 45 days").arg(QString(rounded_ETTS.c_str()))); + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, failed, + tr("Failed: ETTS is > 90 days. It will take a very long time to receive your research " + "rewards."), + tooltip); + } + else if (ETTS > 45.0 && ETTS <= 90.0) + { + QString tooltip = tr("Your balance is low given the current network difficulty to stake in a reasonable " + "period of time to retrieve your research rewards when solo mining. You should consider " + "acquiring more GRC to stake more often."); + + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, warning, + tr("Warning: 45 days < ETTS = %1 <= 90 days").arg(QString(rounded_ETTS.c_str())), + tooltip); + } + else + { + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, passed, + tr("Passed: ETTS = %1 <= 45 days").arg(QString(rounded_ETTS.c_str()))); + } } } From ca14c92246dedeb753fd6b7c0621f8cf265af03d Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 11 Apr 2021 15:22:10 -0400 Subject: [PATCH 025/280] Use g_nTimeBestReceived to track prior synced state --- src/main.cpp | 8 +------- src/qt/diagnosticsdialog.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2c27645600..984c937f03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -87,7 +87,6 @@ int nBestHeight = -1; uint256 hashBestChain; CBlockIndex* pindexBest = NULL; std::atomic g_previous_block_time; -std::atomic_bool g_synced_before; std::atomic g_nTimeBestReceived; CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -945,12 +944,7 @@ bool OutOfSyncByAge() // rules that Bitcoin uses. constexpr int64_t maxAge = 90 * 10; - bool out_of_sync = (GetAdjustedTime() - g_previous_block_time >= maxAge); - - // g_synced_before is an atomic boolean global that keeps track of whether the wallet was ever in sync in this run. - if (!out_of_sync) g_synced_before = true; - - return out_of_sync; + return GetAdjustedTime() - g_previous_block_time >= maxAge; } diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index d62bbe9c48..b13bd0962a 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -19,7 +19,7 @@ #include -extern std::atomic_bool g_synced_before; +extern std::atomic g_nTimeBestReceived; DiagnosticsDialog::DiagnosticsDialog(QWidget *parent, ResearcherModel* researcher_model) : QDialog(parent), @@ -281,7 +281,7 @@ void DiagnosticsDialog::VerifyWalletIsSynced() { UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, pending, NA); - if (!g_synced_before and OutOfSyncByAge()) + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is still in initial sync. If this is a sync from the beginning (genesis), the " "sync process can take from 2 to 4 hours, or longer on a slow computer. If you have synced " @@ -290,7 +290,7 @@ void DiagnosticsDialog::VerifyWalletIsSynced() UpdateTestStatus(__func__, ui->verifyWalletIsSyncedResultLabel, completed, warning, QString(), tooltip); } - else if (g_synced_before && OutOfSyncByAge()) + else if (g_nTimeBestReceived > 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is out of sync with the network but was in sync before. If this fails there is " "likely a severe problem that is preventing the wallet from syncing. If the lack of sync " @@ -681,9 +681,9 @@ double DiagnosticsDialog::CheckDifficulty() double fail_diff = scale_factor; double warn_diff = scale_factor * 5.0; - // If g_synced_before is false, the wallet is still in the initial sync process. In that case use the failure + // If g_nTimeBestReceived == 0, the wallet is still in the initial sync process. In that case use the failure // standard and just warn, with a different explanation. - if (!g_synced_before && OutOfSyncByAge() && diff < fail_diff) + if (g_nTimeBestReceived == 0 && OutOfSyncByAge() && diff < fail_diff) { QString override_text = tr("Warning: 80 block difficulty is less than %1.") .arg(QString::number(fail_diff, 'f', 1)); @@ -696,7 +696,7 @@ double DiagnosticsDialog::CheckDifficulty() } // If the wallet has been in sync in the past in this run, then apply the normal standards, whether the wallet is // in sync or not right now. - else if (g_synced_before && diff < fail_diff) + else if (g_nTimeBestReceived > 0 && diff < fail_diff) { QString override_text = tr("Failed: 80 block difficulty is less than %1. This wallet is almost certainly forked.") .arg(QString::number(fail_diff, 'f', 1)); @@ -708,7 +708,7 @@ double DiagnosticsDialog::CheckDifficulty() UpdateTestStatus(__func__, ui->checkDifficultyResultLabel, completed, failed, override_text, tooltip); } - else if (g_synced_before && diff < warn_diff) + else if (g_nTimeBestReceived > 0 && diff < warn_diff) { QString override_text = tr("Warning: 80 block difficulty is less than %1. This wallet is probably forked.") .arg(QString::number(warn_diff, 'f', 1)); @@ -794,7 +794,7 @@ void DiagnosticsDialog::VerifyCPIDValid() } else { - if (!g_synced_before && OutOfSyncByAge()) + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " "wait for the wallet to sync and retest. If there are other failures preventing the " @@ -824,7 +824,7 @@ void DiagnosticsDialog::VerifyCPIDHasRAC() } else { - if (!g_synced_before && OutOfSyncByAge()) + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " "wait for the wallet to sync and retest. If there are other failures preventing the " @@ -853,7 +853,7 @@ void DiagnosticsDialog::VerifyCPIDIsActive() } else { - if (!g_synced_before && OutOfSyncByAge()) + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " "wait for the wallet to sync and retest. If there are other failures preventing the " @@ -897,7 +897,7 @@ void DiagnosticsDialog::CheckETTS(const double& diff) rounded_ETTS = RoundToString(ETTS, 2); } - if (!g_synced_before && OutOfSyncByAge()) + if (g_nTimeBestReceived == 0 && OutOfSyncByAge()) { QString tooltip = tr("Your wallet is not in sync and has not previously been in sync during this run, please " "wait for the wallet to sync and retest. If there are other failures preventing the " From e3e301e47c5cd8f30f2bbb6859a6c0d1501fe311 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 11 Apr 2021 15:32:38 -0400 Subject: [PATCH 026/280] Implement m_researcher_mode to cleanup flow --- src/qt/diagnosticsdialog.cpp | 71 +++++++++++++++++++++++------------- src/qt/diagnosticsdialog.h | 3 ++ 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index b13bd0962a..f17a0f862a 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -229,7 +229,7 @@ void DiagnosticsDialog::on_testButton_clicked() ResetOverallDiagnosticResult(); DisplayOverallDiagnosticResult(); - // Tests that are common to both investor and researcher mode. + m_researcher_mode = !(m_researcher_model->configuredForInvestorMode() || m_researcher_model->detectedPoolMode()); VerifyWalletIsSynced(); @@ -245,34 +245,15 @@ void DiagnosticsDialog::on_testButton_clicked() CheckClientVersion(); - if (m_researcher_model->configuredForInvestorMode() || m_researcher_model->detectedPoolMode()) - { - // N/A tests for investor/pool mode - UpdateTestStatus("boincPath", ui->verifyBoincPathResultLabel, completed, NA); - UpdateTestStatus("verifyCPIDValid", ui->verifyCPIDValidResultLabel, completed, NA); - UpdateTestStatus("verifyCPIDHasRAC", ui->verifyCPIDHasRACResultLabel, completed, NA); - UpdateTestStatus("verifyCPIDIsActive", ui->verifyCPIDIsActiveResultLabel, completed, NA); - UpdateTestStatus("checkETTS", ui->checkETTSResultLabel, completed, NA); - } - else - { - // Tests that are just for researchers + VerifyBoincPath(); - // BOINC path - VerifyBoincPath(); + VerifyCPIDValid(); - // CPID valid - VerifyCPIDValid(); + VerifyCPIDHasRAC(); - // CPID has rac - VerifyCPIDHasRAC(); + VerifyCPIDIsActive(); - // cpid is active - VerifyCPIDIsActive(); - - // check ETTS - CheckETTS(diff); - } + CheckETTS(diff); DisplayOverallDiagnosticResult(); } @@ -762,6 +743,14 @@ void DiagnosticsDialog::CheckClientVersion() void DiagnosticsDialog::VerifyBoincPath() { + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode) + { + UpdateTestStatus(__func__, ui->verifyBoincPathResultLabel, completed, NA); + + return; + } + UpdateTestStatus(__func__, ui->verifyBoincPathResultLabel, pending, NA); fs::path boincPath = (fs::path) GRC::GetBoincDataDir(); @@ -786,6 +775,14 @@ void DiagnosticsDialog::VerifyBoincPath() void DiagnosticsDialog::VerifyCPIDValid() { + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode) + { + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, completed, NA); + + return; + } + UpdateTestStatus(__func__, ui->verifyCPIDValidResultLabel, pending, NA); if (m_researcher_model->hasEligibleProjects()) @@ -816,6 +813,14 @@ void DiagnosticsDialog::VerifyCPIDValid() void DiagnosticsDialog::VerifyCPIDHasRAC() { + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode) + { + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, completed, NA); + + return; + } + UpdateTestStatus(__func__, ui->verifyCPIDHasRACResultLabel, pending, NA); if (m_researcher_model->hasRAC()) @@ -845,6 +850,14 @@ void DiagnosticsDialog::VerifyCPIDHasRAC() void DiagnosticsDialog::VerifyCPIDIsActive() { + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode) + { + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, completed, NA); + + return; + } + UpdateTestStatus(__func__, ui->verifyCPIDIsActiveResultLabel, pending, NA); if (m_researcher_model->hasActiveBeacon()) @@ -877,6 +890,14 @@ void DiagnosticsDialog::VerifyCPIDIsActive() // of research rewards loss between stakes due to the 180 day limit. void DiagnosticsDialog::CheckETTS(const double& diff) { + // This test is only applicable if the wallet is in researcher mode. + if (!m_researcher_mode) + { + UpdateTestStatus(__func__, ui->checkETTSResultLabel, completed, NA); + + return; + } + UpdateTestStatus(__func__, ui->checkETTSResultLabel, pending, NA); double ETTS = GRC::GetEstimatedTimetoStake(true, diff) / (24.0 * 60.0 * 60.0); diff --git a/src/qt/diagnosticsdialog.h b/src/qt/diagnosticsdialog.h index e2c636372e..6641dee198 100644 --- a/src/qt/diagnosticsdialog.h +++ b/src/qt/diagnosticsdialog.h @@ -72,6 +72,9 @@ class DiagnosticsDialog : public QDialog // This needs to be updated if the number of tests is changed. unsigned int m_number_of_tests = 12; + // Boolean to indicate researcher mode. + bool m_researcher_mode = true; + // Holds the test status entries typedef std::unordered_map DiagnosticTestStatus_map; DiagnosticTestStatus_map m_test_status_map; From d115c0251b0cee3b0bd29c73b68caf540f5d5043 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 11 Apr 2021 19:09:21 -0400 Subject: [PATCH 027/280] Remove obsolete comparesnapshotaccrual RPC function --- src/rpc/mining.cpp | 71 ---------------------------------------------- src/rpc/server.cpp | 1 - src/rpc/server.h | 1 - 3 files changed, 73 deletions(-) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 1fa7ca2a80..35d66dac27 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -647,77 +647,6 @@ UniValue listresearcheraccounts(const UniValue& params, bool fHelp) return result; } -UniValue comparesnapshotaccrual(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "comparesnapshotaccrual\n" - "\n" - "Compare snapshot and legacy accrual for active CPIDs.\n"); - - const int64_t now = GetAdjustedTime(); - - size_t active_account_count = 0; - int64_t legacy_total = 0; - int64_t snapshot_total = 0; - - UniValue result(UniValue::VOBJ); - - LOCK(cs_main); - - if (!IsV11Enabled(nBestHeight + 1)) { - throw JSONRPCError(RPC_INVALID_REQUEST, "Wait for block v11 protocol"); - } - - for (const auto& account_pair : GRC::Tally::Accounts()) { - const GRC::Cpid& cpid = account_pair.first; - const GRC::ResearchAccount& account = account_pair.second; - - const GRC::AccrualComputer legacy = GRC::Tally::GetLegacyComputer(cpid, now, pindexBest); - const GRC::AccrualComputer snapshot = GRC::Tally::GetSnapshotComputer(cpid, now, pindexBest); - - const int64_t legacy_accrual = legacy->RawAccrual(); - const int64_t snapshot_accrual = snapshot->RawAccrual(); - - if (legacy_accrual == 0 && snapshot_accrual == 0) { - if (!account.IsNew() && !account.IsActive(pindexBest->nHeight)) { - continue; - } - } - - UniValue accrual(UniValue::VOBJ); - - accrual.pushKV("legacy", ValueFromAmount(legacy_accrual)); - accrual.pushKV("snapshot", ValueFromAmount(snapshot_accrual)); - - //UniValue params(UniValue::VARR); - //params.push_back(cpid.ToString()); - //accrual.pushKV("audit", auditdeltaaccrual(params, false)); - - result.pushKV(cpid.ToString(), accrual); - - legacy_total += legacy_accrual; - snapshot_total += snapshot_accrual; - ++active_account_count; - } - - if (!active_account_count) { - throw JSONRPCError(RPC_MISC_ERROR, "There are no active accounts."); - } - - UniValue summary(UniValue::VOBJ); - - summary.pushKV("active_accounts", (uint64_t)active_account_count); - summary.pushKV("legacy_total", ValueFromAmount(legacy_total)); - summary.pushKV("legacy_average", ValueFromAmount(legacy_total / active_account_count)); - summary.pushKV("snapshot_total", ValueFromAmount(snapshot_total)); - summary.pushKV("snapshot_average", ValueFromAmount(snapshot_total / active_account_count)); - - result.pushKV("summary", summary); - - return result; -} - UniValue inspectaccrualsnapshot(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index b89fc83d9e..ef075b57e1 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -368,7 +368,6 @@ static const CRPCCommand vRPCCommands[] = { "auditsnapshotaccrual", &auditsnapshotaccrual, cat_developer }, { "auditsnapshotaccruals", &auditsnapshotaccruals, cat_developer }, { "addkey", &addkey, cat_developer }, - { "comparesnapshotaccrual", &comparesnapshotaccrual, cat_developer }, { "currentcontractaverage", ¤tcontractaverage, cat_developer }, { "debug", &debug, cat_developer }, { "dumpcontracts", &dumpcontracts, cat_developer }, diff --git a/src/rpc/server.h b/src/rpc/server.h index d69b353695..46061eec4a 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -186,7 +186,6 @@ extern UniValue superblocks(const UniValue& params, bool fHelp); extern UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp); extern UniValue auditsnapshotaccruals(const UniValue& params, bool fHelp); extern UniValue addkey(const UniValue& params, bool fHelp); -extern UniValue comparesnapshotaccrual(const UniValue& params, bool fHelp); extern UniValue currentcontractaverage(const UniValue& params, bool fHelp); extern UniValue debug(const UniValue& params, bool fHelp); extern UniValue dumpcontracts(const UniValue& params, bool fHelp); From c7b373cb9e9ef60156e7a1599b3a233d33119724 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 11 Apr 2021 19:29:25 -0400 Subject: [PATCH 028/280] Increment version to 5.3.1.2. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d32cdc0fbc..8544370865 100755 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 3) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_BUILD, 2) define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS,[The %s developers]) From 81ed5237e996bae2e400711756d3c1a789530717 Mon Sep 17 00:00:00 2001 From: opsinphark <53201159+opsinphark@users.noreply.github.com> Date: Wed, 7 Oct 2020 00:47:56 +0200 Subject: [PATCH 029/280] gui: Install Inter Variable font - This file includes all glyphs, variation axes and named styles where the constant files did not. This should also cut down on final compilation size. Glyph Count: 2537 Variation Axes: Weight: 100 - 900, default 400 Slant: -10 - 0, default 0 Named Styles: Thin, Thin Italic, ExtraLight, ExtraLight Italic, Light, Light Italic, Regular, Italic, Medium, Medium Italic, SemiBold, SemiBold Italic, Bold, Bold Italic, ExtraBold, ExtraBold Italic, Black, Black Italic --- src/Makefile.qt.include | 3 +-- src/qt/bitcoin.qrc | 3 +-- src/qt/bitcoingui.cpp | 3 +-- src/qt/res/fonts/Inter-Bold.ttf | Bin 714748 -> 0 bytes src/qt/res/fonts/Inter-Regular.ttf | Bin 680112 -> 0 bytes src/qt/res/fonts/Inter-VariableFont.ttf | Bin 0 -> 805068 bytes 6 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 src/qt/res/fonts/Inter-Bold.ttf delete mode 100644 src/qt/res/fonts/Inter-Regular.ttf create mode 100644 src/qt/res/fonts/Inter-VariableFont.ttf diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 117f701c2d..c7cdfacf6b 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -390,8 +390,7 @@ RES_IMAGES = \ RES_FONTS = \ qt/res/fonts/Inconsolata-Regular.ttf \ - qt/res/fonts/Inter-Bold.ttf \ - qt/res/fonts/Inter-Regular.ttf + qt/res/fonts/Inter-VariableFont.ttf RES_STYLESHEETS = \ qt/res/stylesheets/light_stylesheet.qss \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 9db02b2518..8aa26536db 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -101,8 +101,7 @@ res/fonts/Inconsolata-Regular.ttf - res/fonts/Inter-Bold.ttf - res/fonts/Inter-Regular.ttf + res/fonts/Inter-VariableFont.ttf diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2801c8baed..67eede3129 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -109,8 +109,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): * 0.4,QDesktopWidget().availableGeometry(this))); } - QFontDatabase::addApplicationFont(":/fonts/inter-bold"); - QFontDatabase::addApplicationFont(":/fonts/inter-regular"); + QFontDatabase::addApplicationFont(":/fonts/inter-variable"); QFontDatabase::addApplicationFont(":/fonts/inconsolata-regular"); #ifndef Q_OS_MAC diff --git a/src/qt/res/fonts/Inter-Bold.ttf b/src/qt/res/fonts/Inter-Bold.ttf deleted file mode 100644 index 847ffd191bae693d9d934e809f5f03caa22df0b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 714748 zcmeF437iu}`mn3Jt8xsdfZRerfp9v4ats3wcLdb&R=k38D1x-gC3r+s1X?X(fc#oCA&_*{7d1{N1(#g;2){t;736&N`?6k_SHgMQG_# zh;xUZe%A3_JAU-o2BFQ$CKUhW6GobiyY+RlzgCFRnU_zQ_6EYt9Al21 z5bWM)he&&8uMoA59y{_nlk4j8JLe16*zwnoIjfs^PiS9!DO~AOt{6RXRQn|@-WF-i zNAY|2D>%^N!X_88zli2v zmeBP6`Vf|<=x4D!NB6V5OuwJyB7Gyv&3Ylr7xWidzO28(@^AWfmhTu`YYZ^B));C` zWO=P|9m}c4Z7gRQkF#87tY`U@@fyoF46ZWXG2USrF(NGAGe}Qkm$8@S55`X{e>Hw( z8I_vQq?C%KQ<7)0hOEP~o@~alg>1>Pm2AbbjqJ-ZOA-^=Uk+e7NDgM{lc%#hLteph zoSeb(W;v7PEXi}p2PJWnPfDaiZj{fld|p1!@nGRin_1rCyoKeh&O2D%>AaKWUCz5$-s7Cda=vpu%RFZu%L3;z zmMfgASU%!>gyrMTr&w-ulKalh&X-ue=6p>U&NrN-kF(fWEOckY8R5uo=WdoCJ3r?5 zznovP{I_#2M}Bcq5?y?S;c~m&Y}as+4z7BxdMq2b8n8Utbu_;=ay4eVg^Lt(wQ{v$ z+0Nx<+0oURl&6*T~j$e%{7hf>s{BgeUs}Zwr_FW!uCC`5ZeW=q(Z+ zxSnD8oa;H3g|0%*e8Kes+gn^)*nY!R%=QPa4_JQaLPA_WyITmueS*82(A+1wPh`7? zyARtZyQi@|!%gnEZ*h|&?t9($vdneovV7XTkzY5tpJn-+oABLX_v>tLbHB|p;(njy zhwj}R|J?mK%WvF!SbpdJcdQPu>wz@USBw(+z`?`M7PZHZoOHFQCz^7+p)dyYJKG}~ zo_hLlmS>)RDoY_OOBnpBQD+#Jo78g+t{Q#q)uQQyk=I@&Jh5L`@Vgy$>@4BZ^k0#f z*uIN(no!h@9ztjfZjRRw_Y0SJK$|5NQ0M$hyH)#A`$7A}=x_Ab_82c2uV~*IZ_C!& zUR6)k)3Y3l9EuuLNdeHTr>peZ>df)ZFzSP~)-BQnU zw{o}A^WE*-?eqe7dv|+%nY**Qv%cJ&?(V9uAdh`= z2elrzud{1?UE5W=MgF)6lPZ2|pO`-`^;`S0{Bh$qj^9|2Hf&Am@%?SrE}n4pgsW>u z>v-%l?d>{O=a0+0v(BaotoQwG`BUQCAT3`sNN_)z_VK$mlcE75<@n@G!^Ml=MRFL@Y|H}NnjeclcvvJM5FKzO^TvAk!*0@EJ zl?7={&umt=`MJk5C`fD3wZ)!-w3a{F9OQRa%f$Dmv7lD#@XMzQLWEq{Z{L9Tc4XZs;Y16cPjf9yjk(v`cwN@yPS%@6udcnRN15X zE#mw6E#luLk5~NWkF$@(mlc0aJ(9mS_RT3-mz1>%f!SAJ;iW1~2v5-_AL9 zEY511h|Rv&op^gq9Ec;D7JHL)v@_09WxJ(>LuE(D} z{_KqY-O{^#+Uq^GH;zC1#79o5chbu4m)O5_-`gYDb9}F^vES@p?JwuszuDi6g5KB0 zxAN0dzXfkn^0Iq>Y?tKBwwc+Pcg9OY=AD^q`^_3WJia%6W5qIJ$86Y|WK4&rjo(;4 z7Yp7Te|G-f;hTqVE=l=#KI~_CYV-WPXG}kcD_UIgarv{)$T>?tOSj`_ulM~ubMRUE znST2mds#s$%1Fyu&Esi%R@;&^j;Hj-vjXQg9ltRla~G|5Y7tFt8 zdck~_3(J0E$3DJlk-bj+Ua~y7cge>E^KHFgV=qg-V=f(Y=>~t=$l_6XONvHqT2eH+ z!`M+{))u4{q>UXh=E+#?Ic`Z&-l(xzV@KqVv(qep99w(I^H@&t+beRb@|W4|8zb$$zbS;=?r6@#zHK7j9KS6*@DwTavwmwnZg14&=|{HtbPb>BgL z7mfGk_bo8(@A%f^y-4#$%Ff<+`LXxVeJo>`C0kwc_u5C|d-l>U0r`7L)4usFxV}Zf z{QSP-U!G8V!ue>vNw&7n?>qV239}~5pYZsE*CuzLoHhB}$)hG;JNbsmGpC+C^^$4F zUElTk+peFN-}m~^^=q!*5c|Gx{cG3n$R9Vo)7;{@#RYH9EuL}OjJt2T^QJrP^^7a{ z-aX@;8SmWCC4XT4z#DGkm-#mZW30Pr^W3lF-&^X=Exx5YA+UeTuZg2}i1A}{i)S{S z>ACa$JKw+KhdX|lE$56_QZ#48o$t@_+sEv0{x~$%MR$HSx9;5FonOxlvhG{$-=1?D z&h1%gs6pwM=UAAMtM@}0X4gqY?jF!R4g z4s2Q2ODHDc*k$=mEYQ0k3v2rnai2I@EETVDJP*eD|6L-C^cSbQq}CO+f) zI!&+}#2eZ|{Y-73ezty&X6fhY7wejSsXkJ7>6h!ddJWq7Z|MW{9r_RY4f@aeFZyEr zSHq<*G181I{SjLDgY}OLpJD3%v|IQ_U1PGb!l-AgGS(P_XyI=)h8eHYqQAuWhw*_i z(fHYj8rMoq>c%v?4R1`R4evH?pbcNcxLMYgO^g7o_iW=Y@=7_=*d%Y08;u?ESy?P= z$!+poetVCvmsa;jvc3FNel9!7FZia*ujJSA1i6QAH~Agk6Xnl*Pm)n3WOutAE_(Xq|#No9HP3auF|JYQYXn%R8Q4Y4pkW{Lk?4! zDpL+ueN|t1s>)LRyyp@t}*JW~x-!{piORCT&MSDmlUmlvoJYJ?o2 zMyb*ALN!*6mlvyvYNEVcO;OY2C^cP8mt)n9>PC5mnyGG;SE}390(q5Ms20l`)Drcu zoT*l;HS(`&o!ThxQJd5zxlnCa+vNl5ef7S~RUfJk<%8-I^-sA-edA~#mpB?Zn#wJX zR*qKk700oTW96%kHjXaxZ;lfkedKn>V8>wjfn%s+nB3(!({ZNU?KsbIp8UuWaumpq z9Zxx)k)Jz?9XsULj?W$clHWV_IQGaN9Z`oRe{xQDPM5zpZ*<-y|6_X)(scu54rE5g*D z5Pthw{e2P8KM;PyFSyper}eVKvNnlk2XeMrCQP*)*1&pr5}tw$!gQP{0*;fQJM@5_ z&K;26xPCH@HilwVjUo#;z@W4Ho()c5uSlf@GNYG=b#Xt zhZo>QD1t5U3cLz`gJSqQATeS)?0~o7Q}_&tss**74%CICpdQqR25>YqghtR9nm|)% z2F>9ZXaOw&>C{?5Yd98;gEr6>+JP6^LkH*xouD&xf#V?^PJph^4NijY&;xoxFUWx2 zkO_UDFHD9hFcru*jeOIl19_+22sgpa5P(}?Cfo|-t9Cog0`gTOUp4YoyPwiUxOx`! zgZ?l82EshJ7v{qPxDV{}7s4V~3_(}|Ay^7|kPij09$p6WNGFeU@<@LT!tgr00pyoX zeo?~x`dhFa-iCMJU5LO=cn|&o@52YM3qFM1@DY3hq~bM0w_F_ExlTG?s=dL%!Z zqgJxE!(C?u&_5f{KO0=PS^?K>RzS3R z$cGgiUkQmjEwi>dmct6z1kb`|YisG9YgcmTea5}?%3~_IavO2pMx3{m#CaQW-bS3a z5$A2hIYgYd5$A2hc^h%wMx3{mNttEVHsZUD_--S<+lcQr;=8T%uC2AVNaO9W1Kx&r z;9aouU?-&HgZ2UYyWm6E4gZAC;0r4xohA7+ntT}t6CpLf%B7@pr4@3Lsz)>?Az~6D zCZTdM36Vyjk~|Gnh)HGA6Co!e7BIHJd+=!4H5pp9^ng$1GcSLtPtmy6owNk9@l)3Gqfxe8Axf~vXhha6W z0rZ{zI6MLCU_Cb2Q>-_@(?GeWx6^N2Nc}+_l&}tkQk{9em>n41Q)(T`*wGYgPh6%u ztsK1}jIvDq0qbJD*b3--Sbu9>Y&a~_aKbHY&xBjyHn<&T!5uIg=D?jW7ybfw!QJpz z$boxc9^4D_VFBC+_rpTqzD6!Q2#a7b1Yrq;U@5F19*QZ;vX*JaHP?sfTX-XpfSWkx;a0A>3 zGdXV#=O2ii6ERIi&sVy&+#`ehP)pfL6N@SdM#XR!F@~9;JU5Ru)qfG1{utFsgA41|o zrB8AT+cV)-xD9RxLO|j}NPGy14iIS3)~A!H$hEQFBw5E36k z;zLM$2-yfB@gXEWv>%B-v{XDC8TkEUKcs>T>_HCxi1~jAHC(y6;GpxjLix@vEfuM? z(W6#2b#XRzaW-{vc1c~F6RV-ZDYaBF+kb~QVH>;!+hK>5gJfn?7iUu!XHyqvR}%BO z=&-smU&eH5F}o(a0d9=dXyway4)tiZXp@?^mBu`trUw<%!;P6}o*FCL2Cp(^2Uf$E z&DD4fUnV``ej&7)ACHbHj*8tM>j8ak>%0&;&r~O|&Vr%VP<5(kT&6yqNgB_w($zhz z^U0e6%TpSPZ514PC@$NIDHDgGRxoydYFzuC!al>?=NU2(_wrXf7*9b3+=` zfSOn}LoC1UgR|gVxETB}5=O&JSYs`;Lo;ewexsk|mu*P1xK)r{#xsWWtJ_G4IniCD zN`O?cTTeN5>&bk@ z1>4s(5-x+wVHAvpF)$XcfGc4fTm|D{0$dFf!Tw$Y*TQu$2`0l7m;wdxnl$m(SOgv>Ko-z|pnTe;& z#8YPCDKqhunRv=fJY^=HG80dkDK^40unC@p&F~x)!t?L~ya+|G1@H>vDKqhunRv=f zJY^=HG80dkiKooOQ)c2RGx3y}c*;yXWhS076Hl3mr_97tX5uL`@syc(%1k_ECY~}A zPnn6Q%*0b>;wdxnl$m(SOgv>Ko-z|pnW?pc)^IEw2W_A&v;!}+hYrvYIzeaX0>?u- zoB&;+8=M5)p$GJYUXTI3ArtyQUziM2U@DMrc*;yXWhS076Hl3mr_97tX5uL`@syc( z%1rH6AYbv6nRv=fJY^=HG80dkiKooemRjjr9&D%Gg{KJ5nThAj)cZky7ytudI={_h zeJ{+11#lk_2HrCh@0p4B%*1EeAe@xGaO-%Px3Cf+yG z_!(>W7uNp~CLTBw51fey&cp*};(;^qz?pd9OgwNV9yk*ZoQVg{!~mic4*`P;;L1n;D-IT1qSl&vEY9>x`HuY^@l(c5S;cG%P(gR)*nQ-6|Ush&sb zMfx($mRaI@piZ{yRMC@uRzETFYYRm{djN9bL0ANfAqYz# z1WO?g^6__;jlI7l_Wnv@?=Ojcd7o(xvGvPwf4~-cRgp z->NCoM1VIs0o2A;_Axd=E5U>Uq10`#ve;tJbiyA~dUgvXY<+j>hi;H(DPLW}2z zTzC)`!D0x)5(vRk$b)>mlxa{0>OpIIR6e)3FD!!>IH%E{8U^`!e?(5SXfvd2=HXazgvY@K9lRFl3u zlfFEY*d;Wotxe6CHhmHQR3#cUrjuy(s0Ny#by6%&C>Lvc^h_ew_GplO$9e_2${riA z3MzksxIQcU1fdeGn)(C@t!nCVt!h7oUynaU<(f71DVoHdq8J;2bk)$q8hTh`oQGzQ z^RVrJcnkxjR*IdT@wAMmQ(}x7Qfc^+PR2;cuVD}T9FqqVDLn_NuyfYFQ;CgK>D`j) zV&|-V*LXU_?`qpb{?c5tpSLpmCpk(i-gd1jR!2ha0x>J3Y_3(W+{M!}QCgBRSG-@D zt6a`(+q1HgVq0%XIisB&D6Ns&SKVa<&y7DvENh6YQ@n42^lSGzxxl_0#3?r%!W;Qnu;Z>d@XdKb!U9+ zLp(34YME4J+XqVO(d3wjWIgIH$&+}=thz3ZkDW-=r3WHCsWDE}n+KW~6@l0xYs!+d z@yYiiK6t?YPxa*B`}o85=A?}EPwCaM$3OpX_UhO^rjwA%?$85zLNCaG-jE4>pf5~@ zDKHiA(pN{%PL(}5^N{*`VLmK?``~_92)1pz|6ZI#8zWGmjZxOdD{Cz=j=03vm1tKa z+7wAYSM~Ss1P--_C+2gr>nM}nobtUqcoS(YA8rqiDQOQd);iwH(~Ip)I2r67AL^`X z@AWz1|D=yDUaS2v-{vS=tNri4)e*1N{_nlnk?5C*w+Ht%^0{gcejzoS?Pn#phjcgrxfA4l7_Ktb&K&VR!^q!y0%L*1}`(I6MLC zU_CquPr(Lw8aBc+unC@p&F~x)!t?L~ya+|G1(LP57n{_}NFy)aIxjY zUPc;uwPw&9j)4}?5-1AMqX_u+zR9`BaOU_ zH1aai$ct~<%SaWs| zX7s8w7zXhg){Fn$%jh64ql3H-p3lMaId;Gv;bnA?*O>#PDWikDj1KZLI>^iDATOhX zyo?U=GCGJi!-bd8L0)n6!S)W*!l!q7-#x_4?MGZiL&7%cc{b^JHtBgb>3KHkc{b^J zHtBgb>3KHk?J?<_GwFFY?XhY0xGKUo>47%sfi~%ZHtB&j>47%sfi~%ZHe(^D6LLBs zrxS8IA*T~^Iw7YMaylWW6LNgSTR!V#$mxWfPRQwmoKDE;gq%*u>4cmv8XZcUV_uIJ zNt6A=`_G

hwQ*&e6pGgFWZw5$riP>9PCc_nudS)l7Y!w3zXeP?uNV`_`#F=XNh; zz)n@sS^Lr2Zr9p+u}G)q%5UVcE`Vk5D6ECY;Bj~Y*1>vs5}tw$@HA|MXJ8XN3!C9N zD1_(X1$YsPU<43D88i*^Wl#)Ag)pus$7Fb^8cg9h`U!8~X%4;suF!7v1Ta5mUI(&xf?a6ViB zBY@|j-SFoe9-BymXMvuwKj-j3U-bdT;?iFD-)08sU^LTC?RFuoM1e z&33hbmayKMEe!6Iji)Kwo_&Qr0xV$ekR2QOS@KJ)S3Ev~HbBVs_!tu@@G~DkIyoMq zwUQHiFI^@^BbSauw%@YI!Jf|{%pC0b9PIfV?D-t*`5f%|9PIfVVvs`&a`1K8c6<(Y zd=7Se4t9JFc6<(Yd=7Se4t9Kwa1vUGP(p+fB9suJgc2dd-s%X&&R|v#?pN{5Q0(jw z_X}}9+ou-dej)A`;(j6S7vg>)?ib>IAv?tA9_<0hg$H2~EQTN~feOpH}{rNn)$5{Z&;DzWO`U-TO!vJZI;06aU|Bv9!dEo}qK=9_g;LUka3u*&tBI?3X zK-!4<&;X7G<_!{!pfNOorqB$U!!ghTT7m~!0qH7^h2x+Nw1sxyh4#<^IzlJt3|-)O zNQV=kD|CYs;Uwq|J)kG_f(+;lna~IN!pV?D9#=9u05aky&uqE#lV^T%*H7;H$z4CW z>qkcXAHE`O)-KAzoONM2TSm+WVqFZ)MxPjS;uFLTFuO4tC^W>H8Yc~ zMn|n?X0p}HOtxC(K|U1VlUT*Qzp)C@Q-$cMLiAK2da4jTRfwJ{L{Al>rwY+ih3Kh5 z^i&~wst`R@h@L7$PZgr43ei)A=&3^VR3Un*5It3ho+?C76{4pK(Nl%!sY3KrA$qD1 zJynRFDnw5eqNfVcQ-$cMLiAK2da4jTRfwJ{L{Al>rwY+ih3Kh5^i&~wst`R@h@L7$ zPZgr43ei)A=&3?<&uYhVSOJ^hS=el?W>zL@A<9NJl9Yoa<;dGfsjg@RQb06APIF`y zrQ{~$;&##{RsY+1|8~;p4qy(51JM85>`<%JgCm*c$xrFXMgl|fW~f>s?3gS^B9P4I^KQ4MArUVV<7r65%fwm`>*YwqawK9o60sbKSdK(2 zM-Gz5@_0MZaZ8Ujc|0BHyy4FRMf zfHVYMBAW4_F35H-5u=TtFXq?ULDb_B}7oOs+3KqmC2(l6V)+_7U6f$^;a% zxv0j-zrj6i*^c>TiOD}m1AG3Xl%pjv`M|16`3Uh}WFO#TZeTCvIL^Kb#=``-8klcU zns5zV3)jIUm<-IOCYdo%PJ`=#8P%jc>sE{EI0c5nFc=P}0`u)j=5C~PXHnBr_OV>BpCtC0#9ouwYZ7}+Vy{W; zHHp0@^T4*($N`CMCYQqsSP84(A$S-bfz_}E9)-2=7(7n;)kN1ehSd4~>^c1oU_L+O zq|$jJkp|K{mR3DTixAHfBF%D0vmEt}XshS$e2lb%=G=j#^1lmAbXa`?trelxico7ssI?;0S`lik2(?y(S}Q`W6`|IOP-{h~wIbA7 z5o)anwN`{$D?+Umq1K8}YelHFBGg(DYOM&hR)ktBLah~{){0PTMX0qR)LId0tq8SN zgjy>?trelxia0tzN9Y8dp$i;DTT94Tc|fKUG9V1(qi(G9HYh2lbs5oQw|W-xb{_BW zX}RzqEP}-lge4GyrH}{tqPSeEDE3Z+OcTYtpI*%S>BX|OC?;?GW%6PUc|ob6yn1+V zy_ombi+OLonD^F;W1|BC)Vn*0m)ppd?hlbh*;(1qOWLT+}6hVH%J+LbLUjkgk-a+ zgwO+8MCj3CVSoe$4se1C+>iz}peEFU+E54T!ckBU>O%uK8X7_)XberDDKvxTa16A7 zmf(R_&>D_~d~=!bgUlqc0kAK(Xo1TtR5Y!N5|^Xv3hi@9v!Pk$Li6sdUUKF9jiyj>d}vehJfXx zWBKS!p&2xXW1t1JWM(H1(8|*pHKQL3$3Yut3+=!Q?V$s7gigTBe0mo+9@60i=nCE7 zL^uh$Ll5W)y&wa6LnicrK`Pc`-TLW}4QIfaa2A{m=fJse9-I#s zzzDbyE`p2UQt-n_xC}0bQ7{_Dz*x8f@X_hx0NY8&cG4%n)i4oExCX9;>tGU0hAA)= zror_v9cI7{a3kCVH$wn!ftfHH=D?jW7ybfw!QJpz$boxc1%DB_5>~-lcnqF|=lJta zMZ4GmPH=%6(x3*^gj!G=>OfsM3K$h-)Q1LeG&F=p&={IPQ)mXw;TUKE$cBMz7|4c! zY#7LffovGahJkDt$cEvC_Rs-3LMP}9UEp{~hZ6wVFpv!c*)Wg|1KBW;4FlOQM&fz6 z3@!(BwJ{pTz*x8fuuhC|a21S)32-$`gsstHe~kU?>cO;czOP2B$+dTnXdgDi{wF;A)r%CR_u^8g{RV-D_g^n%KQ2cCU%uYhw4B z*u5rpuSpBgl()idun-=A2VoH`h9E3~5G;i}$cF-02Fqautb|qY5IhWzz-m|nkHT7b z3?7Gd)?2ce^>*y{9q=}MguV4C$NmXlp@C~yZ>c&~F|D7A)KS*Qsy_P-L{D|J^^R)D zek1mqV5c|5VsD1tr`4sH1Cv%NZ5Nml4be&s(Mqj2Yx3<}a|hTh(}HLg_FFFYTQ2rn zF7{h4_FFFYTQ2rnF7{h4_FFFYTQ2rnF7{h4_FFFYTQ2rnF7{h4_FFFYTQ2rnF7{h4 z_FFFYTQ2rnF7{h4_FFFYTQ2rnF7{ilqXyK3T2LG6KwUTr>Op;I0F9tAG=VnI7TSRq z+CvBE2%Vrabb+YYjDFqhqHMa59T&3VLRRdVp_#wabdhH+^2|+rB6QBq;Oq>cu-iha z0j+g3Fh|ef=s7WKuJmY3yOXAzN85?E68=&9Y%D9=Q#$Do{EXS!7JT7b;U#>CFT*SF zD*O#zgD|`m8%de2?O^>jyaVq-1a`uE@DF$&K7d{DA?${K!e{V>m2TjZHfYHi_@0gB zumbRu7!Se2@CdAiHSh%B7m3aH`y}h9U;{i28{rwCt!_LEo8dVqgy#Xjw(%kq!4`NG zh!=j*Z2qGly_|0}8^5?;azFV6DLV>@9LxF&xRPzVeMS3E(f-4ND#U^+RJ8TfdUz6^ zf(`ibPbAk)g6_}*dO|PAfZmV^eP9@*`dU{q0(Dhv6sq4f2niX(JnbLCZaaq^QN1qz zFH~2YK;C9)4?r$F2#a7b1Yrq;U@7E5KJe`1ZHT-Lk$+i?Bg!KGvdF(I@-K`0%Od}> z$iFP|FN^%kBLA|;zbx`Ei~P$X|FX!xEb=dl{L3N@vtr|PowTFVpbpf7R-|2PEYnf3 zGz;;3S@@E(@Fi#Q&k9*QUzYO)Mn4S@IxTy{Zry_d2ROk6Zb*X~P!noFZKwmx{6fz_ zHa!E`^bBOvGmuTsKsG%C+4Kx#(=(7w&p?ZY;hu-1l^$r^n_lJ0lgs;>{+7w z!pV@u%+-`K+v`W!{+6=+Ej=%(dGr$b^fJ5x$SdV9MEMI*{%qM5v2?f7Ig2#5eI%n} zX`QX`z9_scc6!HJG8-A!ZjTx9$M)>^o|Ail(mffs+GJVIw&-Aa3g8e7_;y*bu9 zRx4dd94};?I60${Ga5Ogkuw@OqZNt9+7{RfFTu<33cLz`gV!J|8ry9Y<7d%Wwu;U9 zoI}l(L(P>Vd`NQ+(tN2lT!CCUWptW>fk&ORSY|u-=Yz?6kw>ta`@n{9nRv z#4MY$7=k53cdF@0-(gs5La)Gs0Gmk{+!i24Pe1EfI>!1GD{5~6;gPZ*e~ocbk1{Su;np;r`+hK4}@ z4E0Nh`XxmDLT>>yhhv}xv;+@OI;mem)Gs0Gmk{+!i25Z&{lZ%<&>lKKN9Y8dp$i-j z>2Lycg>G;noCMvW2lRwqkO93R6Z$}3I2ouFD5(KTYJid&pri&UsR7EFhy3%PH4bdF zX%S^|3v7kgx&95{pEtC>!<(=T-ip7mfi^MGCMMd%M4Oms6BBJ>A`>PuVImVI+QdYg zm}nCdZDOKLOtguKHZiFW?V4>lWhw{XcR&hjE85xaxlE39r8k%IbgjS@P0gk~b8G5u=oa#+?b zF(1ictN=(*-~cDMzzu0o18PDos10?XE*u5*pguH!qoE-*g2vDUnnE*Z4#xmv6p)IzlJt3|-)ONQV=kD|CYs;Uwq|J)kG_ zf(+;lna~IN!pV?j%{~MvNk>Z3k&<+3<8*4{bZX;tYU6Zj<8*4{bZX;tB*>2h`H>($ z668mM{78@=2@=>N0tpgGkU)Y25+slyfdmO8NFYH12@*(G;F*Iw+=@=j#1EeGVJD~51bPSM=0n#x*ItEC`0O=SY z9Rs9efOHIyj?tyu5FU*Gp za39=HU3H>XS7QW*W{<$=2mN6H41_^27>0lkh=)czG~%HV4~=+e#6u$<8u8GGhekX! z;-Q@l=fJse9-I#szzDbyE`p2U61WunfGCTH2p)z^{)y48~6_X4SV5x_yK-|pWtWsh4cTz`d9c3 zqF~|q7odR-21wvvVGaHj*5F@Z4gM9@;9p^lzHl;RK|dG(17Q%H0z-j+jx~nEsc;&c z4%u)9oC#;a*>Db=3+KW4Z~=^f3*jQT7%qWJ0b3rs)`MN^!LIdS*LtvPJ=nD#>{<_Y ztp~f-gI(*vuJvHoda!Ff*tH()S`T)u2fNmTUF$KZp|EQ`*tH()S`T)u$B3YR8UKJ? zxd&bB!L|%ZMX7gzT^r!%FBwT>%0?V(4y~vm?OK8K!1nb^o=@_8lIN2=pXB)@&nJ04 z$@58`Px5?{=aW32n0X>__3!0_}l_m zDgo^20RFasqW?ruD=Yl}iZrF25%2k3fmK6GTT!;vI@V9)Whllg@%PvWL%;JD+66qf zGcVTeh}YoM3r@VYVf1Gh{TW7ohS8m2bY~dd8Af-8(Vby*XBgcX zMt6qMondrm7~L60Z-&vEVf1Dgy%|PthS8g0^kx{n8Afl0(VJoPW*EI0MsJ4En_={3 z7`+)rZ-&vEVf1Dgy%|PthS8g0^kx{n8Afl0(VJoPW*EI0MsJ4En_={37`+)rZ-&vE zVf1Dgy%|PthS8g0^kx{n8Afl0(VJoPW*EI0MsJ4En_={37`+)rZ-&vEVf1Dgy%|Pt zhS8g0^k$g4Elgb&rY;LpcZI3D!j!o%WiCva3#UpiWhYFT2~%dml$S7NCG1=Q&y!9% zvs0MlLk_;yBI_gJQtw+IsSmAPYPWT-`pEi9ea!wRqK*30;vea)uQ=}$Yo~BInp&Sa zjP#-WZKsZi}FLRAQ@XY4FI zGxRXW{YhDPD{OyK7T$`i3N~Fv8JkWgo&jva9DO`xzKE7n5iO@8T24i@oQh~U7144k zqUBUX%c+Q#QxPqvB3e#Gw491)ITg`zDx&37M9Zm&mQxWery^QTMYNoXXgT3yf+o-u znn80o23kN%@IWhQ4adT9&<5HnS=k>3z(5!TgJB5R-&3q0Wj08e4N_)VsE441&A;D?cL8C(vdU^I+@v2X=k3FF`@ z7!MQRYM2Q2|5aTB*TQu$2`0l7m3SPxIaQ?LP^ zhK=wHY=UQDGdu@{@I1T#FG3M)(T6j)q>s5@e9ZmgW0Z=IxnF$ZP1p`Q;BEL6KErS6 z5>IJvNP`+s6KYv|m=DIsd@w#*Ek5Rh@i8BakNIGH%m?FRJ{TYK!T6X3VgvKR__QX_ z6q-SEI0jlkOYlG|XdT_H9n1PSXajAb9eANVbg;HFAB>OrV0_F6<6}M;AM?TZm=DIs zd@w%dgYhvRjF0(Xe9Q;qV?G!k^TGI-55}iuKyS!|KF}9V#($ax{h&V#fPpXw2E!2W z0rHFv2%-am=zt(PAczhKq632HfFL>`hz`hz`hzKh;Az+h&%h>l7B<6kPzcY%3-BToS(|9XZK4ggNqZ@psl5!Z zz^m{#cn!kvy0sTC&tAMdd-3w@#mlo7FV9}QJUj7D?8G~<6Ys=Myc0X|PVB@xu@mpa zPP`L4@lNc-JFye*#7?{uJMm8J#5=JQ@5D~L6Fc!v?9{##8QNFyHGBhm;9K|({tbKK zd-y?Q=*Y631^u8u41j?^p6KL>PM+xGiB6vAjs z9`Z5wkdL{Ce9S%MW9}gza}W8Ld&tM!Lq6VQ@-g?2kGY3@%su2|?jav@5Bc;6?1cB= zAMieW0K4Er*bN`SCqP}J{}ZTh^v~g6@CAGcUkM*?-1(Sy$cOz{#Jod3gL`1-1P#Kb z{kF;Yh4p_RYVBnXA|G=Q`J@5L3NjCoPdcrg*h4|)Bl0nxFOxZme2nSKWL_d4^Ah#DBe zTa^{{s3Fnb3Ju6yNIrF%cBMKUvbCP-3^+47L7fF7ICdfX7s18s&t!iN*4dq+v6{;q zN`Ha7;BNS<$W(Y?)I4BbD#g51Y608Kours!N#(+Wu!v)eVF}>DSIkSL^4QLYhk3@0 z!smDZmT6Zymct5I$$U(!U=!QV!e(GDCI@pdVOs{VErZPA2-`ErTuwgba`It=2Au^WlQEQ;*rGwaZabZ;V4bxWkKw@J#Og(_;J6V#5+UaEoozpB5Y1o7(SBiw%qE zPmAqOi|tQ~?N5vCPmArpX0b6^<}G~2+hGU14etQHW7<+7+EQi4;n0=}(UuC)mI~39 z3elDd(UuC)ma==iL)efZY)Hn);HfS%4+A47=oiJ4pMzgUr(f3q2?`wG1Q)o0o*}$? ze!O~qyn24TdVaime!O~qyn4I=1NET+91RVj5%4}7UOhivJwIMOKVCgQUOhivJwIMO zKVCgQUOhivJwIMOKVCgQUOhivJwIMOKVCgQUOhivJwIMOKVCgQUOhivJwIMOKVCgQ zUOhivJwIMOKVCgQUOhivJwIMO{vQ;2K?d{&yRW_v^o5gw-Ynh#S<4$BYk31?EpLFV zg=v7ekDv)b_ zMXsuE7=5(|zJ>4L->?_Hhacca_z8Z7U*JFREBppguo!70Km#2Nkf6W;PH=%6(x3*^ zgj!G=>OfsM3hF_9ut)DSg2vDU+CW=q2VQ6o9iSt0g3izdkW>fHu15aa54B{B#efcu*O;#8-0jm8vW>>M1o3kAUpQfG&!ek!`nS4x=Y}!#m9_P>^E9!#bytohaelTyU9E% z$yuut|8G;_-*93x+Ir}ZI`FYy+j(cD^K>T34KHFc#l{C_nRL?5$$a~=<8KSfl+?G&Lg0XAdx^c*NU8Kw0@KO25MkPNpeL4R z>Eu~Dd1jJlj29d5nvg;a-h6s(yzJ+QgZs=XcY1NT{r~DNet14nMgZ;aw$=vw4CJol zliW%iNvy3{XvlW8d`R!uhu$|nQAVM}_bV=~wbo0-FhW0?8EC{K*&`!Y#bzuc`XE%b&Ubee15&bF5pfWvTnA+g4tgy(bcTWkax!CM3w(L)urF zM~UnAzqaKqk@sw6+1pmSMY&_m*iykO>Q-?)94o%Ef5~DI;JkEtXJa2rFIjt{yW(3& z(H?ut>KP@T#P(wdC4M{HwYAj_!3tQ{?K7moiC?YU(ye zMxx#%9$%y!`*(6Xxn+Hy*iLTIgIw;DTAoVpSMHqOb2qj0pfP{XkIBvbr4dMO*+*0N zN>4ew4U2Y)wak*%b+I}pI}sWqH>j;j_X3G6j;@7lYok_rD4u%BIxPM}YMMs5hO3y} zs`R2NH|TS$^b^;g)T3MDrO7@iS?{GvScS{Y_z~;&l67{;8RwVmhf4NYC31hC;U!P6 zbgRVD$SH|wV{xi|8tJ^PlGC$e>C~8CY~r8T`SvC@di;|fU!`jML`+N0D6`sU-QZ82 z8fDxZ+B4k>MBhny;()cBduK)WSf3@&t!gVCr_|iG&y49Ydrb(;M__MdmzNOx=;3}U zP1C>uh3YSVR@HTF`7&jw6)O zeZgG^t97UD;c(m#7OW{188I zOclqE=wZ8FO87sppxKLmtXcM&H1MSq{F4hs&V6RVtPo?n0`oC{Shx(c%&j#S&2;1MOEGQ0Od6G^2+X%`uo1NV{PIx zp2qmu));$T9dc!TnY@3p#3qj&+*a9gWv#YWmR|2K-HL6*(mH;u%r9j_D0@xy?8QC% zq~5l~mL0COF>ccz$UPG6{qpCg*4NRs@m~)&O_6|ZB_Un>_2_w+<(~ zxP@ZXrYWe1<}Yu5C)1W(GE>7owC%VJUEX4^s?6^zO-e2MY4Mn(w#5?Oo_$@v zpZHgN)|iwf{dmbOJEZttNu1)>CPLV=pF0Ki^ZR~(wqCVQ!8S=P`!zW7GQ6I=Jht65 zxlgP;se7EAU2fl>+)va4sS=U+^+0Qxcz&ejR3e0fT~|jKmESWSvNeMAybE7Mt~Da{ zJllrnmsmQL_kN_FXK%-)x_p|Zmav3XT`J4vgE;7YD`s0n?HN_8_)vc#6D<8KOUjOY zYS}An9i9AGJ(JsqzGe5!T6a;0-)o(l8cHlp1Le~x^$f8mu@^rpH4W`wQum{U@pG#$ zP2=HJ&(hkG3@1_R9Y!sc4D5((RVCr{y8pBM4J#fgf5wsAPwnSF68EW|JhC21#El+f zGqHWR>x8yDoN%J)Wv@=u_GORSdsX{dE1nxW-xFJA^WLn82Dsjj2FYTCUUuyGM!cw5 zvAQU>Zy&4t!+C!DmuRSL45D+A=l`~c@OMF7$gV7fE7M+y>*LmAV(&<(z# zs5PnVRk16JD;}%rNO>8n>b%O1+j__TME$W<=^~oqfsKYr53{ka(wBN(V!N`~#r0gY zxU%1fOG;kELrLBQ=QJt+r?Bk{GJnlTG1CwaW&E@tvb`K?6w@#Cp`soS>1|99o2 zd_4cx_7AMqtIns2*WT90)fwiI3u|8%{gJzCWg#cVAXoO=AM04@NQP>Tmi3=%4p98Q zUWMv@nu81Pe@C9E)v=p@&rjv!UjBPADjm;#^r79;`tH!KtKO@u;_40W_XzEfd=0-x zM7gK6_jkH+CAY20*IUV{|CK|BA&Xi2h@j`cU8`1QW$z=7WqvAaJ(v0Epm(gF4*HU+ zE;z(`uBzbnd)&u6+D1M&3f-pu1a2G zeURKv-Kw&rrk+$S+o_(~)Bvq-s!>;@27H*?hpSD?-o9L|l=N;Tw}dDxxyBz``Z5Y&)m65YQ-IMH*NND?LfNMiG@n!PtsQ+ggj3!w`%1719>UH_JDpuR&)9P*YwtP;#tNtMi6*KJ1E$S2XseD;| zraqIes&5<(W$a<8L>qlNt5(aO5gHJVXCI% zOvjn3mLudSP_-RTIi6AV9mS3vsuNYeO&cj^;MRuk*ksF=W69@rTV)%xjL%>t}Iu7HOR$R`CLO? zL)9s+;jZCosOtjP1!|aUjBAVuEpvMSAlD}I@7h@ zwO*a$`q1^EI@jIB-9??}?&j{M&Ug24_fQwOGu(aD2=|BX-Rcte=kCvyU+CIpNPH&$ z1<;34H_q7H^$o=-;w*I4+2SH*wz-(O<(n|K{Pp}ggE`)v%<(>#EAFCv97kEXUo>N8 z_D7h3Xf@xO%*?(<7|cfWl4vDf6K}BGCf;KCF5gySxA=(VKl!%OT+Ec7rqyB|^cGqp z%_G`st+ZC6hSrza$7^UOYu7Q`&LnLT|2i<4xw*aC4cZMHxsmy{n`w74i*{4(KIYMG zsNK&@+D)~E%#U4DE6@rEbD6e`?d96DY;V?HVEaYwMYfChcgo|nm-rvb&f4qRHgT-> zmiC3HqkYLd(XF)~^@hx0)QJBka_NorrffIUTd>_y_wv7)9rTVYJL`Q|_SFXpT_2?L zf1`H2AUxCv=P}dM#rnm}bVR+t@^bwauAQmRkJ z#sBHK4YxSgNHe;#-NWc1Tt-i$r|4+(GO{?*&*(2a#sK~iskSlL7|gXpj3FHH89ug8 zF_ut4X87(5eyyTl#N;Vs3)@?b|Ht0H$J-k(sgQe;#BRH9J7h~pl1h@KlH`;eNs=Q;Qc02|DcdzkavVqH zi0}J(=9ph?KjLRJJ8Dl)p7-Nph2GAerk3fH{KM|Md&H7XL z+@iOG-hn-{-xN%Cs7a($@7jL8+qE$Z8~Rvl?5CMNiWH!X@o5%98en&q+9`NL#b5r$EoK=7`SL z)7COk#(Ld)UBtsGydlb4E3B0w6Sm=Pk!`KER*M_0zu^ocX|2I2MxON!&M``{)>>;3 z!@Jgdpx0aL;rhO{0j?k5R3m9^v^IkN5GNZ+>m!_P!7G$eP?}#XFp^eLa6Vp??L}y{eWjYg8ju4VdZ{= zpEK4Okzt)R*!O{~ohT1$7b7Z~SnM@^m5DQPqOpm`e&cOn|4NCPrZn~(Zw@P1UUV`Q zu-|x1QxUt3Uu7zr%J6v^b{wy15=|ofR54XVdsEd^6}P}%*2PKZ^-Mic$J96VLBn=} zZfF{UzTR998WvPsLlzVW3krno6y?ZvVox~OPSE{KKXJXe!`vaRh6NoUYMZ;vUEmBf z1Hrl53=%kP#S8%rn=0y&O~sDru&KZaW&&a;HpK|_fO$YPGn33D(bPO>9t1tvOa}cB zb~LYK9x*fEbEcUId<^@US29nSCqU1}KIWCoT=NX*XU(%f>=g*k^X7TbFPep-x_QYg z64$~iFBT2WU(8={p8KojRnSY!61Xlm%i;QlSqXX-c0A94wZ@4l=6&7s z>&#BGQ?xQ)o3BMnv&-xP=Np4v@?rZ+L~FCh>=9MXUb7c_z#qUFI~~kHa}XS~2C#EH zS_8tS)&LN#0nvt91EM>%21G}+1~SAAcBWkeu4og8s?;U`qD=t)d>eAGU$7U5N@y9p z1$veJHs~$(XTqZPfpDm0fD<3lG5{7k#lmtXIuk`jY9EL=Y9D|``#@-F8Nd}S1Mtx@ z5T(#E_^U9^GH02nN$mrXj`qPCxUO}uAH4If^Dg3ChdtZlocEmf;JP0Bw#PZ|W9Rl{ zYC{NxHpF4!IzKo^grt^)s6Z_VAX*Y4lUfo$v?Sn)mV}6>mV`*5mIVBuB>_HK65yjH zAr!SCM15*KfJW;9VbOXJl3EW!QR@LTS`VP>d-X+}cP-BQ%Jdp|4Me(kop+tcruKx0 zr}l)9Xiqd13M~o4EeXjj2@5TW(Qq4suf(Y_Q-D+Pt%`QUL!ci<8^S`1`+d;VZm5KI z_s5{A^-u|?uWSWPZHP)ZedSBg)RL&gEs07vgXJJN)Sjq>w)!Cw(PBu(evc_=A1JgBa7Wb$tptfy!tLPn47URw6lvT7P-p?nhU-)6 zDYO#i!1l*P+5WO@`%~EVmu1@@!?wRH+x`^T{^g>qdIK$h*4zR}K?~q5a8{|eMOC#L z?SL3=2bAS@KnmIcYvKB?dKa$i)d#?hXcI_o6I4;3p-mvUO%Q`N0rW4o31ZX%g%hi| zWuVbA_&d^h9PI$j?SQK4UuXenv;gYD#@Ex0L6eQwY|%Aa^wMn6B`o@Q5yO_-Wy|fc z<@VTeC$Z&DV#{3xmiv9!c(T)m?X(9wyRc}oN^)ALdS(Iw1=}Uq!okHF5+RW6F`@O&5pN9rZc zaNdQqDp(Z|PeoX98D+(#m57rsB-?U}t#}r!cp73zx6)zBGGNgqTXe-1U9&}R%NG4w ztBD1xV0E+xz>1SyFU59UTg6tfxYn9zO+*YA+W6*d<73&zd)9KCv5^By|0ZHs2}>Vi zy@gW>sW14pr~*sB4*d6E*OOt_H-P^E?6hV(og8JStH4fg1OE%_OW{~Ma5kZ~zJ_g1 zX4_mAws|+gmcT-5YcDLcW(!@~+7An@VWGc6I>{=>Sck2{2t~HpW7}MwZL^|2DC`GW zWyw}qvQ^Gut6Ybz^7U+$Yq3?XZ_42ukQ`G9_Be;_aUHhDwb&llXM5a??eR5ik87|! zzMSoG4YtRZvpufC_V{ws#594GYG#@No15mqR;CqfB>J^T5837f(;2q8qPfl7hW%H` zLMNCWrYAULr)#sFuEusckL`4Aw$s(vPG8M-x*FT*D`2PZ6B%rw6WBuMv4yV37P>B5 z=z46S>#~I|!xlQhJZc`ry~s`{u$?Z$b~=IWbQ!kO31*&|2R~%36WCgpfwg`CG+FBq zTkD$UW%Dxpkj=g-%4T0>$YzJwX4hn!9Wrm3x6m#j%PrY*cVx?*3(LJxWU|ffz&1OR zZFVkf_9mRb@(C<=D_HJNMJ{aiR`}d*wu>9sW;bD*eI?uMhHSI1W1D@Y*==y54O{C* zY^~dw@6Gq(#`7$66}yUPX(!o9qA}a)_EEOEGTY|L_ER`jMcOagFNp+uk^QnLW3RT? zfPTkb3);8MTXAZNgoXZI#8VGmNVe0G?X<;q`X;u`GHjdCcZZ*I7CPQ}$$1IZn(TBO z+vY^r=C$xcR@q{!9M4uc5mp)2imk6^>#N*kH$^1F`c@Y)?&Yw-nr*OV8|<+S)@*}S zlnwU6HWziScZ zI*b6+N8kTdoHzFx&M$jeR+JCIwoXCMdmOCgv+`m29IWFLa=Sb(Ka?lrKjlGK#YXaw zYNBq$xL8Z{kLzO0th;Kedf?ky^}@G}>Z2y9wy=axssh-(Rp=+Lft7krtyAx*1+Y#Z zs)g!f^i~(C9lDNso%)ixEG*28I#IXMLv#xD6ZH+~6;1?il3u27rXHW3fWF=)@IKXF z=sDD<)6bE`fZZw4Cot}IQvae?Vg&BAep{b`0b30tVe~s>8T7jtgDa)iLF<>(ACRWk z8?DQ%ME#+aY^CUrNyqC?NTch`(BDn2idD{)RZ>Yp4wmT1%a&&Q$9S=V9kzYlZWuGt+w0dE9y2dW-6W zwTfD;*4tDYtiL(0IImdmpw;@OwU*ke)&{g!H&`DypE{pfn`u$K+xGSum+}-YO>zH??ca`;buda8UbpmDo zLF<(FnD>~G-W+d^QQkY=I-_~C%kh5ljv3cG?)?M3L#%+#>x^1Z1Han%)kk@6geyiU zL>v4%;dd+QK@a@;3Hh7tpbnj$$@sjT4|+D$s^6A+jKn|Fi0IQJ?Bl?x9(vQKC&%nC zaakv`@W$kPOsh(qUe9f!mUI0!xxH=QDFxGS=$^ zcL_gZx5|dIIdEwI)Q>b1%pM^bC{vAw7`}h1u$A2zt%K8-SmGEz1PD+jc z(SPj}XqJ>lDb0cTDIFtSPniRppRy46O3E_e%9J(0HYuHex2E&}`l;%d zG7$8ikAXwwcFygJ()fp%vV~kzwg-MvcBSkM=sym1sW?*(r5p`n$QqP_8H_2XLZ~7k zI}`^j8>$RU4rKuILN$Q}p;5p(p$0)H|GuFnS!=;>5o&jiAL@ zl;Gd~KgOJ8S;vGpA9Ge-$RR(uHFN6_h7O;jea`P==ve6F1wMP__6Mg|=ro~EUkY>D zq&7rK+N3H*m(l0=m~as%;+k4MH8F5a%}T8f9g>wAV)W??u~ur`z`uXrY?X~W{xPOD zBiGapm-2Jz82)>%eyaQw`gci1`3auoT$)q6q;@CmnhM*LIyiM0urPHza8l|t;LOz7 zz=_Y z$mr957Jo@SBLoISVxXBPW=$nbD}9dkIsYD}RV4p83v<@|XP8zatv1pYU>eg^qd2~6 z&PAN)Q2#yJPl=zZ3sX`*tr27xZcnB)59qX8(|RDa0k&bfQxqrSn!7f41M2%FF{ejP zKS;Po8lz8NjA;YOPc%O*|9no`h_s@BlQto3GSV|W?Q!6owE4h=X|DvXemrT*(xB_o z)}*Zu_-R`xuhX`qeMIQfe*%wY;=XA|(_q)q!nDu%-@>d-mx?)U*QH!9od%A1Z`z>? zV<0|wr=svgK<8CYk3(;!dO8>p+UNXlVcAQ@Y+us#kJ5lR(<`SZ|4)dru$P|nVocBY zbNuus=`C=VXv|Bm2~M5#1{d(7{al*vnmY(#yXKB!WIDYw(dlThfbYBh_b}?dm%f*u zs&nbgEnxrs(~&l$v=?E{bWqI66}glz1-ZwdC7Ax7FdZoghnkoU{hWRgW^|y~e7xB8 z0?ydqiy?Pv?hM=|jOpJ7boz|+Spglm`aaX=5 zp7R-X_YLWr5D#NsVqOUJ4syK!w_d>6pMDtih5bik`muDh(8BSY&OmH=i5Xb|C)(%5 zbVhZ$dxpwzfj;eHLPmLT5-GNf>J-~~uF*cDXuk!XS2wRA?#`G|E2A#LGUnCIi15R% z;rz|$4nI+O9j2RQw1V^w`(+^0y2wxRMVv@!@ngt6nR^;BoXmw!A2SAIbYYtL8GVSR zyGMNb{{Ii4pDI6v=RF0|hNl?(znW%}01P zT$uR^{4dOe#mO8&Z6VMT7=8Z5h<0Nn)VZ)hJkkCsPM`mHCh`=LM(%?*pOEOV>q;t1 zndc+Q(gmCg{NM>-FZ>=>mQ68cu4nv+aXaHK#=VSa!@qUVjqyzVRkA3Lxs>IM8T=5USo zF{?kMIVh{(eDpOW>K1E)bC}mGYZS$kHI`$;eRJ05u%^jjy+W;Ye_RFE8Tif0nhWHS zbKHFvhXVSeCI0BT|271^HgL?F7`HO+U__d;_GcYNC?1FO#~H$7N@sFlxw1EBa|z6z z1iF0oVqhWj6SG60r)AFsW@T3gmSi8KIJ0ZvS2w#Muo;b-X8ZB1CRd-v6>`fi$=(R; zk_|~_@8mc+hU_Cu`=O3A{mVH{PE1Z|`1f7=WDnpwQrZS*4+HvX8_%ir(^ir_8~h+` z^FS})r&yZ30`%{tZ5{ikv~AAb2K+bE=4(z!v?`^Ibg%zb$k)t%PWc?T)?hzA)+U;C zZ*FIz;94JaO{N;GH?7x##Qme}SXm zXKe07AonZ%P{6r--{%<@F)m?5UceP;4v(JsEgY5uc(6FWrSSE$pJveI0}bZ$!yNAY zfzHCOdLHUXxVP0T4|N3pIxu!&?9SK+D6Kraww6v~#@h6@*{Vx?%&;Z=fH^mlE9U7C zP9csSMjTzwCA<>rt0ZP76SgH+gQXjY=QMLLDGu>hB-%+P+Fnnzy@}}(rVp_n$~EaE z5RG}ugau4@BGg^^j(0NlBv*YW^CybZpkH7*&%qoW9Yg7{+LKRf82PkDdkS}~O0=Cy zwE2bO|Bd;`W$>FboyK$)`&r3;{>Jp@Oz$AtL8${CXZmTT=QI5#)2o@L@+O_VMB68c zwofsi)8KF#tVH5l$;6lAD7Eq_LN${3s*ve7n0}L=u0A8J+LpQ^(WVB`rY6ytVMq8a z(?2tPiv1+BpAhpY-=)=qXgz^w&1FFMBaWQLcbv_*jL_=G{`+&-I!xCk8nc=SZ)Vys z?J!-5=|+rAh=W;ggngKv#PnpMt(O^@V{wU6FA_&RPkz*keD}ABBOfPP&nCX)r!y7U zHIDHzc0J2<1M*`vpiufZ=EN{3hVRvw>BdaQF*ag9Z5g?AVU+>Ju$5i8gjl~2ZK&1& ziMBcS?W0UTM11=prZbt&WSZ-OeFM>^35R8ASdS2GJwklv3!{Up=7nO?*6OYHM8?CK+}(oCNuw6u>{UkjRiT6LJN!*mk4nuUz- zFfJ3A)oPZpt4AFD6XR(@n`{M;T+Ne28%~MSo@l2%@txyLA7`5VJHG$T?0++dUBUDU zqV?Mx|I5VDeof~xrfWbQNS#D=S=T04>m>QN4m-q|$zew^KERv@n1=3!pB&~4V$Nvd zs4ti^itkdEwboFk%3VwkVgB8}t-yVU6D`MZng^4s9K)PBOwVR| zC?n^l9RC|uXvuqgq%_EyzhRc79LwqH@Y`g>IfVHSbDT{%p79ieP9q6e2Y;hbJDI+!wnk*oD8aV#!rRyO&u<}+s%<3jR@S#Cg`LmZQK_5kP`nA4OvI**ZSlk9<6 zr&3?b_gX@dH|5!X9rn+)K(m(6>3sJOIsW$uF?SeAHNE z6+~O(nd31&N7_cu;ZQwY1J`PNuWH1x8e+wUv{>(9jUL(PJJ{zWj%^~*YBh6MPpZE& zXDy+ZPUv=~P%hUd{ULFzB#u9c>9$O_CE8ia$Th;5Nuiv-GKXuWGmk7RX0npchcNd} zI*&5FjbgA9nf{28TPDu)Oiv|O=Ru~q%-F}7p3D4&e3$o`vx1(~;WFba;(Ph2D&Tvu zj&j!Wy=?Z+dFm`=`US?=5-GXp983)po5yK?QueUVyo znrf?6kIR5x+Mzrkd2YpNPOWt%`P40mFQ*f2Q9Bv+nsGmc)u)+0O|EWNqRllii^1pg zXtvJg7jiYfkbgCnXvM7sdC;Xg%e7vkg%5fJ$N3(mTyJN335Vq}jMdNNrx9^+TcD;u=y_oJrw8&WTuv)DDn^f=;6&JkUm`P>H9 z31sPv&7pYasWXviYdojwd-AEiC)(OZw6zU1uK2}>)4H8d&6>CAED{Fw9yVB%70-h| zPw1wNJBLKYw(Z-5MAuI3+lIgsXU~ch4ymwe+d|#2g@c*m9^#1=aUxzMh*GFMWe`hw zQ9)G1I;+Z951xos-&IADNT$$#I*xLej3fC^rQ?rM5$fH0&@i#CFX2ahdk?)sZ13Cm zo_w*Z?}(aLii5yf;uP>InE0)z74RC_16WrU0PD#~eMekbQ_cZiDVOvc zbl077UBAIY?vWe&7xeBcHxC-pcaS{7czp2idkW+)Lkjv0mS^(uN7+M>1{F7SXw55C zSzs+y6?m1(0@hYFhYs&ERMj6kJb$QaG>rT=FTA5*h-!nUS1bXnml4?_M^qEl`9GC1 z4L3h`q_~AL9K7HIO@o=rC@uj5SK{3U9+jZrLpj_?5gyW~B7kKd(@OpzsZHaRqDO%u<6q&$yl)yNYzS3Os zDx%aSqQoVOkl^}^Qc6C#h6t)%O0*MP>jhh6(UE1)i99iN6Daa8MQ2coDfASi=6KjX zVcs`+Z^_tN^bs3$WqXTP(7CPur9YmzDYGQIS>~ziW?2>StBqf0{6^q6 z2fwxW9l}^wS^WBEFUb)()pHu<%*xrH`*{8B*VeqY!?mNXD|_Ra7Bw&UmbAF&+j4Tt zRp))TwMuR^sMV5ITUw83v$Abm+Zt^eQ>#gidql@$thtec4_7#k_&Fmb1lEVvcxP%oWc-13W9{i$mgjaajByj))({QSlSb zQ~J9&F8(1-h<}Qc;%D)T_*I+|{}QLgZ{mzNDI&?XawXP-T%~HOt5qF!4OWHJQ}xxgs)4!=t0woTy=ouU z9Uf2z)pzQU`d%GYKd2*E)zL-YqHoo=>8`q)?yhgwJ#M>Yh-Q3)0XP9oL zySbfKZJA!Cx9Ma0nz3e_xgYDYM$)P*Gs`@Q^;A!pr?HaiSz1Ab^;0j~iFOsN4oBa4%}y8bqI0Wrn|R6T=5!M;J3X8p zVli})U!tTgE83#;EEY?}3b9(O6C1^5tkc~k_M$``L&-Uf5)voN%E~e%vt)HyOV-8i z9?fJM*-73i2Vx!WG&xT$m22fzxmO-n1Jqz;sUoZfnylhfMU<%=l$i#qnQEuHVD%8W z4-30b$Ew+dY6;fAuEwgNjcT)wgB*9E?3~a-+d58IDg`Uv)|;P%!V0%zzzyc_zz@uE z;70Qg;D_b}@FVk2;K$}9Rsg<<^=?1o-zOM#QdsXsD_%>?r{)y?`MAaW3;tG`&&+9D zS70^VZ*cqEoB?h#XR*Gjv-tvwNriE{EkS>2qlB0pHZ-~U%C>+zZG-Uh%-6P!f4gAj zmHEbYg^SgXN#$?XsXk_AQg&Nh2y-8 z@25Bx#UT~@GT3|JkX_EMWj7WE>ps7?%iCAkO%PvGbJ(tcYcunMjh#5i7ZC11a#75u zwVyxQSp8Hz`SJ>z!nP>~RdWp}eL376;f{1iyZ5`R-8JsJ?t1rq_gD9{`|q)NAJ5;5GMd^jdhWyw+YDudUa?>*#gz zZt^;NH+x;YTfAGn+q|w`H?O;QyVt|(>Gkq@dwsmVUO(>+ufKPvH^95g8|dBb4f5{s z275!ieD7Ybz#Hlf^G15ByuW#$c$>Y?ysh5n-Zt+GZ@c%UcZj9MEe({hIOyiTxVy1a z!CUHYpjSahuTrbL*WoJ;bz(7gHF#aEgzjF0`thFHpgw?%sa4DCR7>ZG>%~NwFAHHI z7RoJ3LBnswy48ky9oBcAv~sWlbD9|jJHH4knNQoT?1@e%r^uO(b%|Tu47UqfZT0cw z{k<99x|pJvNil0 zr&3$cz7IVyWlw^{749u@jbSdei{l`Bx_3k1FvRZSp5;YT{3{ksfJLGi;^~dM#36l^ z@q`&@Ia7%Cz(Ua)I36tnN+Eip@NaVri6Vs6$JIp__SFq|Kl&re9s&td+_wOW&}*T% zE1_0K%fG)EBnprNW5q-(N%h0?MWSSU6DM`O;M3oX*8k~Gt3RsAqAD&cF2`amT!*4S1 zehDiJzg5UDMhuY#nZzN&WmBeumoGEPU1pFwO8PmO)j~;XD4NOY?5hT_5WB-ryq5!q z$SZ-xvL>)dUI8qSwJ3I34fGgDz79e+hZMV@x7#13VgyRzBrzTNHV3WWh1mOUnOG^- zi1m=n7Dx*1Ux~dWs65K9d@YdO_&N%GFs|*gJ+7$jvJ~gvFIxidmF<8dWeea4*$P-Fn*&Ens(C|YBj9jJb*fl)1QtouT09%o zwNdg$;4s-5I8HVOvJQ|`%Ja|5F|2;Z{ir?^%bvgjN#%Z&r20d<1VCHJ5uo$sNMI3k zMW7MFB~XrK*9pLpay+n5jsp(Cdj(46L}0Nj1{TRloTrp?RiRO$O0b-Xr_YCv$8asg zmFiT!oDM8RzX~;0P6LjXj{t|rM}ftX>eEH_PR;?hP-53Kgn1TtzkC`vL_Py7mUDqc zavrCnvZ#eM;Y~1_&=-J}*z#fRiuR0gL53zyi4zI7+??4Dys}eX)F>Ip^0) zs{Q$LBXG3*7&sieDUx5(8AXz6e?hohY+=_=i4W@%t^Ml5vNjiOBx#j=`6Y0)+yNYp zcX$-nPGFJz8dxB|0gjSi0f%AdC3?DVfnzSv7C(ZQFMk3S%45LM^6$W6c@$VAk8_Ta zMTZ_hj&?v@?*3=;l=`#s7sQb-(f@P4l&66^;amoQbT*Uy3RV+BA6zR+&6$dO(@xW246mXa-4IGEj z1K4j>7W93J?ENHF5y*8{kv+f9m%K^>ov*3_BlcT0#lK?J1X!e?3C_3Qiu6amY6~n> zq+Q0V*1*xK4e$c{tw^U7s_wx1RX5-ebsMl)k!C4Uu;^j?{m1s_A{(ymMA&>a1UOO+ z1ddSm01H(A3$% z^)Ton?CnW1zrfmKo&>D6f?h$GXMm&CbHL$hKCoDk9x75Vu-w8HO|4{?*MZ~IV&G)7 zwSqQ4*q#ODSS^8jzG4l6ok1ye%YlX1E0pfUxA4Ogex7iybbs8PcZFY2N_M5E5x#hU6_k)m2ypcTcg zPJ;Gr_#d@}E-H(H+LIf^CQQaaigYR9y_#y*NSy#2p{Xtw>J;Sqk2(ZAs#AeKVZ;eC z(`lfOY1CAEs74)i<22Ry;hO4Xv91hGDP0M4k*0cIpp${4bXD-X>Uhw@bQRD&bQ#d& z^ktw|Ytj>AbRy_A8a3X1ms*xO7u@r#way@p&H)zV-8IFY1stWbf#Lks4M6AX>wv#w zt#xa5Z4Vr&+X4%98{iP#4mebI1QzQKz#@!KQttguJ4;g?%-45A4sU6y*M)iz=zW@M z;&^=raI~iSJ491WFV<8ae=jYl?+A;!k5J=)_iH=@LX8Iw*Y^R7brG;gkL9$G#s0T6 zyq=8E`TAktz4{5@Nc|9Sgnk5AsHXr&V=fcj`ElS-joR(ndM41-j{##ebOLgC0$hjd zr-8+K7VrW6Ah1Bs1&-2Ffy4A1`0TEygC3`+0q@gKfwNi{gC3)w1fFN5^)uk)Ytj?N zdLFPqKMNeCNoNFk`69b6V9o_KQ2zy7>_QG4tzQBT*RKML^((+4O**AOF9AZ1)QZ!j zIfiS}2Sw;J`n|fSHW>9HG-;)L{T6VvCcQFTuK^b8w}D0aZ@>cm4seuS1;p;>z;Svt z@IJj3c!AZ{TfocLTY-i8bKq#b4Opx{0~YBoAfMjh)cp)jzWxO`oXWm>fP8?u9`5nj zr>ZgD+H}U7rzseL$4-*i1u`CcN~g&@jK4RLO|kFeec0)7Hui^p5&JuSfPEdeVGqZj zRcY+;eBpQn5HXvC}8rF6cLhM=bE>{W`P_XQFSm0^{I2 z@h;_zER7V@md(Rs-P7fKxeRyTE)U657~f9DJCP=;v+A!#sj1inYl&Km_W}FWN$p~n ztLhl*=%D*xC*(0lLhA2)fbDBqS;+S(|6o)kB8K*@QhjjB9r)3m}H1`>&RTQT*b6Q7n$}p!* z6sIh6NcRO2BVB&RAq^OC$Qqo%Ji2onXnJruL~)>}!RZ*qA+3ML=@i9*H2?>iG!irP zJvg1CIMDjw+#JQJ!kjKqoT|*ZC5n^8oLi$f$;`Peij%^eu2Gy2bGk)wQkl~|iUa$G z^xPiBNoP)vC{6}*dPZ?FnbRwZLzd}`gZdqL_H5?#iQ?oir*9M|mpT2SIC;#uBZ^av zIb>~u{Ho5JJEJ(4GiN{)rv`KGisD?soPklCn#{R7igP7%21RjdG3TBr&Q;7A9L1^4 zoFP%1YnW3I#i`4jp;4TxnUf#Isl%Lmqd4`LGc1ZzpE<*sbL}NY(9q(+=+6%Et=Nxu z!T#Gj1wEP|%gaQ0{`fj$urJJ2C`3kkg;-glrYr0YOG$an`j zK~{302V|Oqx-at_)OUHkgE}sUJE+&P$U$9}_c^G)a;k$mD`z{Xr*eUVx+&K?sE_gk z2X#W;n=q^C;e=D$LA7xiwWClta@3Z(Eg#b5J8p zWe4TQJmR3l;+zP|@fRJ`I8)w1O*G{klz&sjL0vYb9Ml(cnS+{c;vFgp%N%G16YHR^ zVXxHxw{jM?2007+f}Dj-LC(UCAZM=!_H-Hnr;v?6&Ncx)=QIVr?=*wtUFQa1ywe<5 z$+;1jM*18%+Y)#^>22g}YhaPn26&&-7C4pkFmkp%Z~^ICoTXa)q6008oP~Bp&O)al zXQ7{wv(SOaS!i$MEYO>@(1x^ajk|Ih5+B6k0xvSqY=j+MqfeLvJ^jYKU=d ziP07E?}mA;Fp?+HOC=umTE+yW3Fq2N@Fg*_P45yv16tH4{C#&xGO%de7w&Xl?AMPm zxnUT8T@H)K-ch9K$mB+Hm0)V}j)FVJ+2pUpW(@XGVn{20xR}|4Nz(h~9C| z!00bViY0#Z@l#$OKH5cpWqo#Ya%!FX%4CG1`E@}kOE@`|&wVAeW3{le&jerC0Eu5D z1SD-|9}K?uNB#}g#PJt>W{KZ9-wWWj(*L4GOn$BG=jn@abBW(MpM`LlPG5|zl0ThY zm|ci*c8T9PUl<3^9)R_In7ai(D!Yql0v}DHd_d=BH%AYU++}thxTb}#0{xlja=a7& zXx=uJB`Vu2$85|DBiVu%f4)oB5$xZdC>yX_sSDh;phld*H^i=GE^u9j9p_8}4MnF7S~rY*^IrSFB|}l`inpMmVs*kuTni(YSy{rB zouj{E1pBLUzQ4>f!oy6R^S#8b{me9Gzs3DA`C)gj^%UM+| z*F;b1Kdr+1wer6^x9fN2dm%i{T*1sl%)BUrIU?muMVtyjF~&qat;{%x_-o-u8cm3F zgj0NWgupYkf#2ZnG{Z0+|D;W)90V!K5oIv1XeB~ijoCWaU^Y%Y%(uA~b84=$8{19n zMs`EYCy}BI-i-hAcQrT7?%=r`QL$!-QkWlbqwW%O=OD3II&ln=rc+Zi&t$V; zf7)S|5zSlz7UH>TdFSj$8I)(dQoX!}Jna6|)c4sZE$OR)V>P zC)634g?@KgSyNZlS+c&asq14dUOUV^?4t+i5we*cuP4hkdWN1YJL~y+ktmR>u;K&l z1l+d(ef!0tNG_La#3ZzOx5{1E3HuoO`4-+2%-~eb;Z!ZSFjZm+rAn-%REc#LrV8Vb zE=C-;>B<;u+@*7LO|f6swXsjR>4fjpFx4SKwF^^_X@m+~J3!ukh;|Xzc94F+BiDt< z8wyERa-plwrzfq3(T%lu&$$i#ghS#uX873{B^<#9cT_a(;M#7R_@MGy335#< zTfX;`gHY0n zs_8h@?s?4aTA|jekJL8xEoS5%SEn%}w=_mIGjt7{4%bAt!MyHXdLTwO$Lh&AA#*O~ zK`zy+^m>eP?$CSn5zO%tIGv?D_R-I=YFQ1e=2i#nl;6)9Y>mRG=QL}UHQ!o{Q&866 zl(MbZIsc$_%sPd`UJ@{8G7a+~>zGEEC*2vd!UvcFQ-o7EXddJOoQJW}tiv4N?Kny5 zusLDQ*e>R?R)rm{37g!^ZfDVC4x?+CbU~iN+!JFpI^5%Psz2)8-ZzJa6?eY$K$GlTG&L$zIa!guG^_V&_jbd8G zbdKo}Ga#lQrZ8qg%+#2fF>_*`k69eEEM`^Ax|ok*w#Mv?*&A~><~ZiYso0pN&+bOndY@gVHu?4Y(u@hpa#?FkL9Xl^}0ZzpV>R3SC9^rL}P~9R_ zy9m`aOhKbYs37d^C~tmz(5MkA2n&rG=25xz{dF)wSSrH-F9=JuBj5#Lp;5y;Xw(Q5 zgoQ?p@Pe?=s9_$}3qL++)Cd)XZ5Ihk^)B!igoQ>8^Po{9R1g*#HNp$RLZdQ|Y>yw7 zC_gMwepsUXutfP`i3-Ak4#xt^6`_K#ft<;z1z`g@BR3*p1342f2-_iY-$2gf8r(OK zGug)AzOY>36a;c6*WkW^oRN~qeFHfYF9;jRnRr21%ykaO9LSmMZ*X5&u83L0F`L^N8A9LD)df#0$a(awc97Hjp#5(t@yooQW5N4dhI`AZ#FK#1;t~$eDOS z*g($2^TXn<;WQKF-LD)df#0$a(a>g@8!Ul3C zUJy2rGx373ft(?gNZ3Hm#0$a(awc977U}0af*d1Z1342f2phlD7;9=>)6U%Q2`?ZQ{zKS`FtkzD<&@1JDq|M&fqJn274)4%%u zNs|75-#VEECu#Zreg7mW`cHE5 zufBhhk^kTK@5_f;=PS`hC%mwOxC;Adg?YX-x`nN(fOtz{x}F=G!N^Q_Y~Ro* zm}So=AHVB93*$nVxnRFZJ{Y6o!Q5dQ<7p%MV}!ps-Z^%agLtM{B1Q!Bq%CU7IILi_ zFf#TqMk*FSTA=Kue4T19p|>vfYrus(|8+m+WRw>tu=0`oO|=){o>TE0S$LmNAM--b z^+Nsm-Lo+Mun;Q@u(r`&AGu3FJsIZBK}?Bi6o4J9g<8c0tLGgu$$O{A(3`c&Bc{CA(~QYsV&YSS=KoO+OJtgt#P))gK)M-c(z*XtV7Af z7uUe2qaJr&!CC_Tf(rOp9ZP4lVdU)`XP`5|YavP^9HVY^@*&AOn7thMGO8K+sNKD^ zsJAq>s)@d93opS-KsgV5YL)2J!fKS@zlAs9<5dhW;26AbA1vz2UvLe0mVH|87hP@I zizNK4_F=pqD=%8u{#zu4QmSy?vA{aHgE(FBd#s4t>zs7XI7hK6PB}m0;Pf`muUHW` z8U5p_-ap*?AbGqW$Gby%gYzW9KP4xLCvf`UVOdNgiShtO5}L?k`UCx;{JZ|rN|Pt7 zBgRvWFzQ`a^)O}4HL5?I^{J-Osh8?uyRF?rO{dc&)qH!d{fv6f{=oi7yzonJgK8H}%X>&2#c6pn)iKHgb<%m#nXP_y<~noL zug-ku6?Mv4;=G|9oRIf{F6H)d`{>K)Ogo+EZgaoTRopM#9XiR~C1I>E}g!ttI;`+y1F;U8>27x?(^=` zHN0`&IDG||<6hXtVQyId3g<_n3Orr@o-mu+?^)LwJw+ z(k*Zc)GoKsEmYr-#MHO$cz3+o?LO!}s7l=D+~?FD_XYPQwHKo`OVxLDVzxTuzUjWD zzIQ)%H>o2SzuBygy4&6D>L>Rr_iJ^`{l@)9{R3we?o%hckQY+FLeFKXQyACDQD?n8 zuNqzu*YK{;eA>3Qyn0?eZRlK2ZPSUK+VMtvh1&IsJj@yP#(HCQtT*0!P{(;wyeYao zopz`z&}oO*XYd2>16|46?d{f;=>$-H8ODXa(^csFL!IRP;2qP+bp9b`3X_I1y19v? zA6AL@cy~qbmq|<3fG=eoq_@>}voMd!nx=CrVV%Q!%)zB5$A|ry@D|nA%2$Rt zsP(oRezRK3=b00*R42>{iSmfBSB1IX!ghX3Us;{`wZnXz22H2R(^vZM->we3yn@uV z6Q|gBoiJ}U;+}2Q$4X3!`LD*^J z$Fb^tjG6~)|GavZb>%CpD_><@`3~#KwP*vZSL;|~ZefkNjWy<%tT9ViV;*FU`4eeO z?UBaRRY+s%B+{5VoiwJ-AdRWBNMq`3)|jV!o*_<`zjjS+%(kpC z+p)%M&lYs`+UF*~uwyooht7uJ}!vc|lPHD*`VnB7=oVjUi&SRN}R>BLXJv^N6B z#+Y$yRE&ORH0=3(<~~u`EHDeiW#%QbNF>_*?0$Gtt&ppYjUUdAth{e%u1Bm=uEnM@ zv;8Y##!0}KjfA&S;7$Y=S8yAFtMS%inw$mxVDNDTzcI&vnx!$9h~Ah|qAm5&tCK_esnT^snLP;x~a3D<37^2vYo{F*N4oEXR6CiMF%ks}R10T8RJD z;-8E52Kgqfp|F;62L6qYPy$w;Vi}C@D7XxOi%$u>gQt}Az>ivWo$)2w*C7hI*&P2! z6Eq0Z0zDauD-B=LDpjLsA+82TYP5;zeZ4&!@lejj8h_s(-sF&q8>gCRRmwqn2y~O4J)zrrPyKnwEohkgzKQ~IQloP@J7Boj8oDAf&*m5Nt2u@5zsza#n>l07+US>^h3hr5V%{-b@wqW0H`?4$!pl*THeSxD^9SaBP}`*mewJ zoGh|L>Bt^G;k|q!dyED9i%IyQoq;erjduV_$3C3cuM2OW(;e*3h26P)E9l#+fv}SQ z;f`*Y6ZJnqAzw+ zsw?ineoBqSbGo-4A{L;ZnJ!+pGOdPUxz*TeFE(4_tcl_qYqs^2*k?U$EfWW<)z)h9 z8?~&(8EcQVN1V0xS^H324_e0h#2j z_1=}q)LWAwI+b3gMwJM(9{!v82kF{>Cf}k`M0LKh$-(Y-y}+d%zLK3PPL~UOB$_H_MeYXB!2SJ2ZT!RJIVA7Q5(gS^kc42;mr_inp{ik_CcZ`;MRO}?*%S5)sUxfvg|pK&FB7A z@09Tx|1q3L^%nDIOGM1sPU5+<_?L>G=yX%to5Xw(I(s%|(Qmg35wl1wf@q&5H!1}9 zgs_^Ob~*J&nlgO4{CKuXg6$Ue8O49AJnci;2U+8YK4L6p`@N@rQfGBCRz~(jjUTE< z>T!Cio~EDEd#q=yv*wDVrb#W6+9q{K>XdYAQn#c#lIAAOPkKA)pDCqMDx@T(WT#x8 zazjenlv`46OX-$!d&+$&52n19vK(iuf1dJn%1E2XC64C`vCHBxJ(UYpuBwSDSMsbf+nr#^+Vng5#l zM(W$CTT-{CZcE*rdLZ??)Wc~i%}y(oRyHj$Ejg`PT8p$^X?LV+n1ssd+3D5N$EQD- z{!sd-8R<9^@Y9ULna^jwp800x-!k9L{2=q=%uh29X8x2Vvh1t|S-rFF$eN#BF1r%W z%ITLqIs2#VQ`PKh<*HS!UQ~VDpXa7&=Xk@058xL3vs$asF{vdk5kt1{PS zuFw1^B4cxrjK@R9|AdUo{*H`QNnAt1>CCf|JlpHMi$DZUp z)%Mig({N8umT@R7V~81T_g@+R3JU!=`uz5lf5m>vPae8oh(q_`Hy%HX*dEF|lzS-c zhjBt2sJG{i16K*L@5tWd1NHY#-8*IPWFhu1#Sd5ivwyV^2Osw71A`G}O5k#6$DwV9 zJ~?pvfhWP8`P~h~-@EES!GU{scilhYK>VR0`~QWzHHB;30k|Hhwg2<|+xPD}Q1w9h z{ipYz+V?i@jC<{SY2UnkPwZ>6ug1RYef#$9-S-K2@9i6~ukXHINXbKc-`?A5Z_Pcm zDE{EHNA4c7C%$A?$=4;%mK2qYF1cd&vfZ!kp0In&?x**BBgEIczJBlPJzo`lG4kup zU#9fEqW6|w&u8rnJrUZK`VS$}X5ja8x|8ur#t%Yd_RqXCa{|Sj`D^BHS(SvSG^W%m zv44#@j+SO0m8=I_y`XKD*+-!|upcnqFLiLb;@Z!dip~MtpqCQys~Y|#6Nf&;p-)@` z|LS904{$I(5Vt>G#3#l>W5r|iC%$w1g!sL<((iEm@r0O!7Ux1GG$T5piGMxE2d^Q1 z$W`F9XueOAi|=~ATLN-2>|%Y=Iy!1YCDtgb2 zU^o8#6>arRTa0wPWER-H|FG{By~pN-J8U+@c<1%z345xkiB{fErlob%v@vhneGT?S zHV>K?OnbA~?r)Zx$Eb&E_tXzyBy@s(4|=>S%w)SgMnpTH=kM>4Zoyi)7|CrddWqh! zPJJ;J_A%DbZg7faHSBSF1IFSS%SPr=%!*$qUy_UD%UGrGrOsDz*aukP9jT+rV)ucT zm^phVRw(pSuVM#)*VIc^I(7hv(XrU=(9yP;iTVEzVvg(-JwdOuGO+JLEcOJ7!@fiD z*cGUaY9-pL)}oziBigIBqKmr48KXLhe%R&a4mD8pS9gm$)gUoI-GfyK4~a(=_AkU< zHIJ$3;sy1Lcv;OApJ4x&&1$Lm6uZZ4QLl^7@cw10S}Q%ZUM8rI-IlVl`bu7=cFIKc zHQs*hkSS`btgcSW%hhkP23Ag7t$x9~pLo3aNsujdDLG7EBgg92a-41>$LqH8A>B_- z(|6F?K{;LDDWB4#r00?k2q9YNc+(PEI#F_o44(ZaG?C>-Hg^~ zmS5-<@`T=m`Mi_dR5uN?ip%2tPEGGScZ^%)-sdKX9;&DFs7zHGoyTQEjj<$oR%}(v zWh-4;F4d37zv>xsnSNBhu4l^S`Z4rcN-4aj$39ODu`|q!-E2JcLa$dzvM+XgDWh!kv=YQ!YB1iv4H2JXf1hn? z1>UxmlkIeQdB1KaC+PO_4gI)Wp`VbC=mGL+J;wdQ-7XfWdG4303+5MpCI6v65#^L8 z?pFD-y{;fT=!&wVt|U9@%JM6{N`0w*)x*^)>dT-v(or>+A^E)St2bjT{u^ zj8=EZ7_}PjU>nOR`UbgMzo{yrxAd86jJIel)J>{0dQIpVSvO$zb_?r9^|qSgR&yp> zuj9SmQ)-raQoZ6f#oNGbXu*GhH*&|+-_gE1g?Fk?=-SRKcaU4Co4e2H8?guY&AO#- zL2n9mAGfmG+^ynXhSnc;$H3dJgJ_v7(i@yt^{09}-U5a&%eszR%QJjQeHy!h>_qiF^Px@)|v0ESGU|+b;yP3|j81veN zJw}H+zncDdKmD-#kb9MTwezv_iL=@H2=4;!c00LQ?#phjcL1}w>pE{^ruX07>FzYI z#4Y2FbNkwN+IQIl?ZNgPb}ziwp5W$Td~Lh=+O6$wa5uUixgWY8xCh*W?sx7XSGl)f zciDW}4b&dyj=&7`BJ3kK3VX}mYZqdd*qiL3&Tf08a|*l3{^Fc)>p6dOm$|Py8||^~ zBkpqdukN*O1H0IMz@B7Jw8z`y+?z4`{6lB2eZM`y`P!cB_O>5%x4Q2+UpU*`SKU|K z8O}HEYwjZN5ayi^cdv0)WB1v1*rB!=_MNTZo^t=??>_6E#awjhY;xB+JKa0o0kVOY z?j1%CH`B{96J;#TmltWKSQLq+Vu#$L_gZ(OFVF&WKfV%U((6f?C-HA>uS~%Yp$2=Y4Ixl1QZbw8-hp?5flLtL@X%QM@8%f8)88arHG1x6??&o3Rti=L{TBj`#bmCt=R;8 zKF|CAyzldTzW$cYEi-4%%$+$ich+s|w!`dE2QgT-ayyzAVGgOY+Xb@{mzibG3ucj= zkGaHeWYEmV&MS?b7iD+nB@=6p)IZ`-XN{^Ab5s;lCf#HYQ<|o1EuNASooBK7f3365 zbWH6$Z@Old*~H8-o0_?JqoKfTZniL*AgqR(`0)f1iD^knBhh;^*S4#d;2I^#m@E!@@ZCVQJ}-0tRQ@?5+dx6@qd zT&CNZEzPggZu2Q;nfr^(GuN7ixlZPq&x?o6SH)uUHM7wC+AKD|aW2#a&i#5he*0#N z^Pu~MdiOxm~(Zy&fB8UskH7+zu^!?5~dZDx2{oQ%o{nacrzn4Ar0kW5l$VsZ3 zJX?>GllAFt56rB;te#h|;60P|>P6>V=LgIZ_I4h@?D#Hom3-HH2lIt}5E-Yq_?w7? zzvo52cOXQ8=(0P-or?XB&lP#teeVF{0CAvkpg71lNE~Gh6hpE9-Eqb+F$_E49gltS zPB2asXBcOR3C7vtOzc!Q5&O$cGNy*kugS8Y7 ziIn@NQyDIYbr8V^xuys`i!BR3C{wyW}Nmkes8AmY1r*@>(@cUZ+l%*Q@bzo;pL` zpeD%M)l7MZI$z$YX34wM1#+djQLa)q$tTs#a<#feKBaDzFRJ_GOX_~PUOgmVRS(P8 z)C2N$^@w~!EtYSorSfg{nEXUNg%vsPVLi?UxlO%q+>7@nA5~8qImR605_h`1$vscx z8`uvL`x_P*QZzS|XknP5CH6-wGz?KB|G}>&>=GeaD@tTkl**mrEJO#-Hl~Tm#<}7g z>;^c+I8RJ9W{7FVOmVJpzIY$I*?nNFl0#LoJWhq=FjXRtSEcd<6_zKeGI^3JmnW+V zIb2oBQ}A}@2vsdds#fx8b(?%f-7eRtJLI$KPWha=ORiN5urc6vRt6iciI7c!zS6*etflt%zORY2IZnFz+_+G4D0+ zQ(vmD)YodO`bK@LzEj_u9o-AfBi)P46Woi-o4bk-@Od$C-%WiW;d*L z=z-M>z04EMldw0`aPt&%ggMeY74J_zBi4v##dBh4i_8b{_SD1XBj#ef zYxR@*S^c7ZRllii>UXmbqNnuRGwm^J+uNOsRVG)sSGrf>-K*2g(dHO)thg9!@17K^ z#S8A$?tk3t%xQQhYq~qnJkOjVz7SvHP0g>}8}NqG&E`DpPc`4Y)tqf!h&Q%wlHcHc z%?eeo$M~cK0s#Zrw-kXASb!tPXa+((hm<{xs_heTg*= zt2D;zi>>>ug_zeIswY{~t#fr>`%0`Q9A-_h&a@_4XW>n+$gCl#f+C|!hDl(bxW zBh9Qe|Fj(QZ_6@9n!lTWS%#(D<>n4EYVI_5;n!DJxGT*+%xKFyP zY5xuTMEeB$7HcuyrM=8rVlA~Eb-%O+*~i$&+C%KY_R;zly+GfoZ`3#F+w`saI(;+V zB)eYUrRV9J^n87XK1;u?pVF;#wSHJXq?_u~^a`D)-@^RrGOUe#95bv>*bmtc+mG0b z?d|ps`*&-d`q)}yU5nKmzu3Rp+w9-$SFld=Rr^hkm+jZ=_4XScAKD*V&)e@{ZRkRK ziG82_p#7Nfu)pK`I=nmG57F0S>AiPvzK`eeXclibg6lL#hxs+V$$eaZh5heO)+gxU z?mg}`?tJXneiPPz-s0ZrE^u}_tM#+malUfC#;U=$ohaU-%hp+XkY1-x!an(~Q>(AQ z3PP=e*lqtqtS$UVufdM{pW=PH#@JOqpx0ve{g0hL^&9#br?b<=sd4tPHaI_d5lC?q zA|>94_DTG=l);<(9LWqLHq!y0%NT1cw4AYMoR25~fmJ*RLooJGXcc3%hgLJz7-%cd z8rSRrZNpfLplun8es8oLV@-w9`2h3zw64Mfb7{1e!UH>4&%^m>Cx$!dRsH`xwvzeCRtw zjyw*B9^^xNc2J~=7!D9=@Syu1>O=P>n-X^eI-htEP(Fdw&?6X`4W(!Wkcj?ZKG8!` zS&_YfYy&-#k(7_47&!$x2po-LWL zfVOHdsEShXdvZrsC~4_(06_cVx3V02i0$vAif>*&?K6KvOjB_!R{20i#(09QGd_D;JJ|p*s ze!$2G^h3tF4*C%z4}g9QKE=5Up`S6>-CUrD=b`94vKPR-q7a)IBM9BX$iJXpFa`wu zC=YL+s;}@F7!LGn#%K!N${2;vZy1C8@LTXbjvo#EfiXrwe+2MN>;WyrFO2a3^j8M6 z=R*9(7=J>yf$d0h0Qv``C=K*Cpls+4#=0I#c>s#a=x?wSpD%;%V$?;@T1KscMj5jL zx|>0SQ%HOyL>3eu2t+-FgkcDJdQ0Vl`$&_)-aA6VhX_PRv8<2ro)bv71c;b=&l2x> z0-uyFgD9$y$S2`FKj_{7kyY>a3?-En{F$CT1fs1%He%@cASrzS`=bj<_Xf@kXbyv@ ztdJ-(g4$`B%NXOJc?`ALGM_O{hc;uV{if#}frze<%^7OTWeWyTULjjD)UMyX6^qQL^|Ks^NfS(Y$}5DQt#2$ZP|Gl&`sdcJzkShOb+zCs|9EF{X0pmtGK zGR7UyDu&uQS^APVh0v%Tjw>Mhkt0+DGU+c8v+WqStEY9Tu?RHyOV z9Uh2a3)zYB>UL**2F5~Y7sj~)TEiI6LHF@-pmeD-CGPoRB#sI2-j*eP95ABDh9cS1%O?20F-4f3G!Ie;B|h9Oz(1ku8t$nFSrfD7rt@CE@}wlu?wH<9udAhcSxMb-d4&&=VLn3VNc? zRnU_dMfK}spR1w68AbK$6rXFLBN#>XYoyOD&{G*T9ZL5gZUuB*pw5HRwSaRUl+Fk0 zTqxB^;vq1OQ5Qq0o)Zs)@r=3xdWO#<& z@&<~0n93MnCv73ATmfcyguIZUae};vaVTA6FJMHVmoUyk=o|)fLPB23c>Vj!@ELgH zhq?F+j6DV-k{V2dIB3|HC+UL$76=+0g44 z>|!PG_KXK+rG%Wv2s-ZuM*R()&lopDZ)7lgCFD&E^=oKM;*k%%g)v@*-pUy7L2qO9 zQPA5NP5yg_&pyyQ8Dl5(E+6uj1wIEtDL+8D(0dq%(sD1O$3w}#3G##c8Pyv~P=(6Dau{LAKw(DDrKx zFG1z-0i&*ke(19dO6>?xl$VcvsC+(Q)b-GfK9q(}88r|38G~6>f!_o25RIXm7(+uh zGlJ5&g~9BrkY6wq=*h$evVYz@R*S>qGbbjxor_-}@W^ z{ee+A&>wyJLw{maQ|Ql(GY9$$qf4N_GWbOnA%A0>^Pt-piXX_|8O#_9xt$U7p?@%# zMHcc;hQ^B&J0LWb)n7gxp?@=&Z5D{Xdk7c0lfewMKuq3)VnVW(F^Zv4pHrc9K2WV; zBf$vd9dUlfhT;!|K;9AiXBt7_mjuNj49n+CD4hc+CPCvLkBLx}F(KZABCQ_Fp#g@* za#Y?PsLwRkBB(Eexd#vYPKz+;-hlcwG{zuQ9u$7#jUg_CQXYV!V<_EJZWlr6e4vhm z(m8Is2iZo7;00D0!Gb;HfQJ=Vzgk?SZGUz+HxA#5o!h$K1%l1??Xcj#T|?i zpF5$Yj7B{(&@Ol!3oQfXI2ZNJs9?xm>{2U?Y9F#|D~94g29+x@3AACT z4`{Sy6y>`eL;XRcJ)_759T@5x8XXx$_UOb=KhfySD6&l#hS~t88oe2c1sePM90KjbNU|l`2!fu0)Gr|v{M?8z>P_hW z-~e3zQ|N&V#X60H7;4uI`ZvI;31QGZ0L81QFXC|;^e~3TpEQQ|cpBP|QMd<d4_*HlMxPBG;zMa9 ze;Nwt*y9*YiVx*$1f$8nMl#M^ zD3t*)ABT=&ocEw)N8%PR8jJxqg0Vi2K*uqf%Ib8+xe7YoX9M&MpAVoDeBOti$!N;! zL@){b3eNKR1xofJ{sEJHc0kWzocU1lQJ~0|r!o$ua~gv;^9ANQJ@k1{@)e-Scj(_h z-wma90VuNJOh!@JoX@Bs&{>QkpSpliWdGUVLX`U*(2E#FHolnAR0l6%G~IU&qYI&z zGP(uyGDcC_sce9vyi)lAMaN#jxa5;pGVWF=*&V2Rp;t3%5%d~Hy#)Oa<1B_=%P8{O z>lj64bUmX((0Pn5h2FsE4p6c?(A}V9YoNP8Z(^KX(3=_iPUtO+-VD8!F{w`9#yFI} z+ZnwDN_Hfk2XsF1Hn@v%C|wH}6@gOu0o@UL52HIn$ri-Pfa(%40^IL&3Unc(--JHE z=#@~i9Z*xD5BgB~JjCd7=)>R<-1iG8*%ass=%Zi>pu8?+tkS3*}X4%vXx2dqz^s~GE3=#yYIz9*k~igEsiKFwI8q0cbR4(J+2Uk!bh z(d1LlF}eo2meF0I>llafLS^#;e4f((BICRYB|ii9DCom&H<)Ae^aDm;0R52Br$Rqs>@m=f0c>xNgKlK(Goe%lz&;K78DmqLKWFq@=q5&A1f_ce z-3Gdau_#aEPr#zIe92gJpRX9b5AXr? zeuw5U&R@`c#@P;S#^|2V0>(joqCS>t0b4DDwqzWXg(_t1S%?qI&83+ERIx+TKXlDk$BPUcB#=Z<%!{~RR`!M=>XjjI0AKHy^HbA>GdNH&I zqaT5iO@Y1^+KX{ehbRt0=q1p78NCSFhjG7!_GR=7(ES)a1sY*A-E)7&c^Y~Eqo+d; zWUxPvPzN!ZeD+{Q(|3n3`fuo=jLwH1298A9$bXJvoY$d)z;J=(#?Vt3{T!5R2b@o! zBLRHVg)h=Gp8DE)43z2ypg1@60|=LV;XKAg-K8;%Iv?LxK*_g&bqSQp9!RPy^lxC( zeaLTseFpSWMwCIR{D4J1Mr91dOz7o|SPH#@5g$RXV&rMiYZ!_LP@KZ!3n-NzFvml$ zV?-EwJtN3w^S}*&t~s9(l&2dR8gr|g7(wNJvk%$r7Djvsy_KQ49d#Q+YcbUAjI4&< z!O&cRy3=PP^e#q@f-dm+6nZz~#bNKoXW}z(A473Fbw49FLl^pd4t;>3I1cr_C?C5c()%9u1}Z1Cz>*$^)2GZjUi$C3G2Bj_>z_t^h0X z`6%cr@FYHugRW-uq0pxolgj#OMw1<$Vf1L|8pb>y`m7Jy5k-Ey7T1K&sdeB*e7+d^ z5@S+Xz3lTi^c8^i#-y@(l`$tkUt>)89K{Dc+Ctx8O!C1u8I#KMEyk^ezRfrvK;L0B z<(1kXYGeKc?=vR(3mpe&$1a*bRwxIz6_or5I8>(O6F`%V$q#@t6G~+dbR#I0F>uKD zsa%0R0!n@WoCQ#-4?xp3e`WMx(BBwMWw(v-#*@F}GoU_`+Ro4%g`zs}7t%>K`I`}g zq5m*6@27S$Zb#@Y#=Hnx3wGn&PO!TG692grYWS2v72~`B#X%lLP>Z2Bx@iLk=Wc~+ zMh2lS2!MQ$<?tw zo`sf!3IH2XJtUmxp;bO)r)nRv6UxkN2Pog|K}SI8rThbmW0{>n4R`|V{p@!a?{YD91ttFj4%IudDR6&*k{to9kuk}3#9Bb*O&H(~AM))546Uy*@Ajeg?H)!v z487OqRp@<;SPZ@2=QZd;pF${=J@GYIAJ+XV1-W*O65d+09N^AL!b2d z5W3o@5tPaUQ0&QTqo0O`z%xGILCJ@JGY$GI<9rW&j?v^lYZ-^qNM!@uZBQx?LV*{2 z=)4yhhw8^mKJ?woj367o;`2ImJtN4*ull?JrSb#LbSNDMoFAd|Hz2E^Z~D;n-eM@e z;5`H07D9B!XRU=ofxgceWGk{Y5ObkqUto}LeB@IC{g~0@ z8=o-FJzOadLe}!)LDTR_h5mYW;FtR7~OGY06r8)^@ zFX-2dro3!r(=*=+6xGwN0wq zK#?C%x&igO&EFVBezA@5`r?#l;Pu_N<1=u`$Npd(y62x@2ku3A`HOKLf&R_7l-_?B z>U)|yeO5u~-$0^_%~~HS|0vjvYwn9NXGey7v-m*9LOP2NWi0q=@nPU_9EUs>4`8fU zp+_j^njDXLCa1X+u&n6=d zgj$S2pOGj+90YY3gFb6Uzy=|gq4}y%fDy=dD2p+W|4@(-^f{X`hCv%Kf<8B9jN_qA z7(t(N7z6eTHDv^S&Si`fp_ES`&VW)rfpHeJ86ze@3mBT$2{mWLna~yt&GUp>GGZdM zkTK4I7BONHw3so-&M-6~u7s8_G_MjWWyFopFhg@4p)y9?4=rbCZXr~`h=tHf##jKY zV#EW`YKG<;Lai8a541Hyb0(oSj93J1%h0?;s2wBVKcV&v%_oF9Fydt>>IFe_51~$s zq;lxYP#iqeg^^SyH4MeXL;En2%BL$s@$pbMMv^VNGsdUT9*iWr_GBn-9qPr<`qofy z#`qk%FC)p`eHddCv@avcmisZrHfV&AWY_%}<98_eI*?>z@^4^lhmubNN%=mQQ7G2X zA&ev&9LgxFPlqv*>~T0l@#s)LMv`rgU??^n>d(l*&;bm^Z9@YYc?|SOhT^!PqZl~^ zI*6f|Zs=%6qKygBwSXcU()mD6h0-~IVwNE~4#;Uxs*`}?mm#W$fY#xKsO|xZBZjEH z0eK1Z1V+*QPh{jA=t+#Cw4BUH^7-M6qI8|Y$ZMe^7&Q($lA*PAp;H-kI&>5xuZNz- zsPWLz46V5fjbRkk@v)4&0ZP{eit0FB3&`7{bUsisp>z(AcR=YlKyl0v)qf!Ggid5A zrWu;VNUED>F%;hnoz2LV(8&zN4nyZKausw6Lvh3ql?RYdLZ>klQw*KU$kov448<2i z=P~jr=nRHpjUmbdkS{_h-GJhPAxbNdFF`4NfMSH9*^FEdy^x_;VCW)7z6zzX2NV|! zQF#MeXBMI|1{6~aQMm&7I`lGz;)bEQjC=!nIYTkT&=riNx_>1@al_D6jC>nu9+dn8$lsvk6M*7wA@Tztw?XGK6o(5@*#Mf04N-Xj zMYf?b02J>Gk=+5sBSU0sVB|n=V`$DibUR~^oydoPOXWs>189wBh88KFtVXkmOvChsr-OJ?aV?(G>1}o07F5kjRv9x^g+fjp;X2|w1iT* z0>gq*nE_DMn<74 z7*Psc$;h41Rg5?b`XobZC_<|laW?cRhSpPrsBD0k41I>7wH2W?j5r7SEJJH7LeDW` z3Un<)>n%d-7(w@Xo-t-XUtk2?>qW+x38i`k1l{XphSp_-USS0J7}YUA^Wvdb8Sw#> z>Jp%N^3dyy914Adp}Dxwn~XdTN_7U%yj0P;lWhm0zNe#FR=pdT};9Qp|(Plj$}R0Z@?Mh=I5#;8i@=ZriBx{0AV!q8?$ zj(~1qR5kPqMvjDj$*5M)uNa9k4t>qgyh>;*BcFkO!_XW{=vzio`G3dId`swiMv^c5 zz^FT+KQi(;=uZsI!-RfjB>BiMj9LJtb_dY9-q3Fh&Ci6WeF2he^E*Rx7oqKpBs=}V z&^$)yPezi>b}%%j5&DafWWT=|MQQs7>_!_hLr_%`B@V_duv>`+jqwG_sH6$V$M=Ur zn}cF}e?GJn;8==9mcXVZ9q~QtZb>K59p9e`?E&_~_bBI*2sjvi(hhnU7=V0@h8_jr zrxbH4f&UQ_WmR$lBT-f*CovLbRWh8BD65iFz!;o+2Xri$i0jk2lNgFSm!QrQ?uAg) zbAn>5C8*nkdl3}%nV^_x$rQ%D7&?`qIA6&$#=Qi3E~C0Yr!(#x=y?pqw@S#Sz`YbY zlc88q3E2){7Y0!>8$c;0UNRT_2ezt#UJGu*_a{Pc26y56r=bhL1NeR+bP-sB{KD5t zmVzhnJ=uE&Sc%VlpsT=YoZA-q6eGwcbS)ryL!SZgN0;so|0{U`=Z=ED1YXAHG0;~4 z+8jYPL7P*8c1Ao2MLRuHh34;BwN16xbvVNfDiFK z+3zE;5!d_z`WfTi0EO=o?v2n*j7zrJ3~(&P5lg7-fqN_TD*%5nXG6(XzQJcY_gjFv zD)H|UvK^2!pg)0MaPDo;ZD2c&e-!#B*n!XF+kY|g4CvpCBtQR$QMhi&PKM^jN_H_c z-&In}xG2k#DC44zmkN-DF*xj2+Jw<4yV4v+?+0zlSc9OsjCB+=51?*ZM?;%2F7jPk z!030N%|QzsgEB2`$yi8#X(6L8ffj)fju{6nVKmCR6p1Sh<9qmRX&GZ311)E)q0kCO zPl8r57V1Q46=R(Xt!8vzDEyDGuY`62o$>iNXcw>#?ll406?Dhvv!Fc~XEU@X=!Ng+ zKzlQqKJUv=tfiD}2dr*Tx*yQzK*^3k!|zHXjDxnkbbrR#0zH6n-i02>SjR#SVr7;81OKNx^xDDMLq_de*6jJ^~~WsLi~ z_e06nz#==-IVa=$N1($Q`zk2?4OpK;$#%ec9ZLB}nOLYNrRRZ}`1~XEd@u{2Q71|- zV61nbv%%#k7Z-|lxfIv73!vnq*Wfex;I)kP3-mh1`W<>bW7R_EF}4l8fpMRJ&S$JY zp*MnCkcPjZw=z}~dKBj3z(5o1yqx={<}^$B=!2MQJ8G0*c#}lI?(HLKiY7 zrR@QR;(w)!7|Ve^$e5I#hZxI(KFk;+p^q>WTP$77Q0%Z2?M3Ml+{=J2W!&Y^#~6w; zmM&vV%H!hz_Mn(zDIEuJD^a=vtVH>eu3{)=S^6YHam-TkAz=OneTs4E*ryqbZ2k;G zG11aBjHRK^GFAY(5qye!(X~GVpTp*qwl5i%u1)>{T)OtJjEifBk=8J5Z=VEJjEyo3 zBNK#uA{6N$>=U3U8^XQ~n#*YTa<~~|-vWi75fyP#bd0sjv7W-OFd81<5{kB6c@5-#i)9?aMSq4YOk z4}wzOfPD;<@(k=_p=1|e4}qS{*mRBIjD0lp6h>3tM=*K;6n;kNJE5mC`bOv|M&AHE zjnQP!(Tpbhk74w6(6NlZ89I)k-+T+7&gko*;~7mhIfK#jpc5E<6ZA|*&xcNA^c_&N zH-tV5>Z9Mr=d&686m&A9TS3oZbTxDeqfxiQQyGmq7oNuGrqFX4O?jQp=oQfO7@Y^5 z!RWW3GXea?eHMBxW0O6}7QkKxr91=sekkP=*pEZWrobj&qI?4T2`JeecbnVZ&XP_2it%IWe5$a=TW5$Ag%5oSBzFpRov95)-WURH& z5Mw*}E0D&Dv6urYgu;g^ zaEv3MxEA3c-<1o%JvbL-Qn`$=UxA`*3Hx;@&L!+up(`01_pe0x65jVH=SnJT`X06; z?3bY^8^V4K`W$1ghr-r`{RZ>}#{2$7dS(q;vr5c_>{M*zZ7* zKf;EeRiX?CYY6lg#@;1F74lEmpF%BQxh$%Y9>Rrft5KH-`)=r;jJ*(wxMq2> zyBHT``T&k0T;%=14vY&s!*m|@Cs5SK%~|;3CTI|}#b@~N=60YTVl^euBN*dhXn)3l z-)$bi(7O(s2QmiyX7iB@y`!-CC~zXKgPU$XiBShZPi7SEv3WS7j)9)SD3r-&lsRO_U@<;#hd#ukpDOx|OlVLVsuUCMewlxHx|^{Dsh8 zK~a8$J|4P*!LO8x%_w6+p8$o=5SnZQpCMe-`OP~S_ZsLf#+?s^uMqCF&?w_lnNr?> zdwqOM9^u}C&ysQPgu(|1cL7u}4(_wXWSrGd*oDx~LgBN7hJCgWF6s#KwI#qfZ$q;f z2Wi<7WONf~Hls-!F`ABT%;-T-Iu2;~&z2lU!*{lzju853XfC61?=5+ZgZytnIT88_ zXfww7846z^w1%R-5Sr4}f^oitwq%?Sp@od|B(#WeK7xiAy#`vsIKMzk8Rt`In9*dX zGDcJW${Bqkw1UxOGx!jp*Fvip=T~So<9rNl#W?VrEv*^-2DA;MpMkao-BA{F?H-^H zK6i!o1t;V45a@6)5}!LmPi3qP&{2%@lMp|{HwbH{5Pv?;*b{|_B9GDb@RN6-yBYU4 zOg(h;5Egy~@;CgR+;4~D`n{p2dkE`s=y(rdEyF9?M|lYQ9_Y~?!hS$lvb=V7U|e9f zP{I*eqOr&ocO9{`{HWt1U4)%29n*HkU{R~I4AUATj4X-YSyW>hN!LX!(R#rEfjG(V zfRxzsd|+XuD;5e%*VgWs2F}JAQVflqDCCjDB5j&92?n*MRk%%Zo8&gl2{sNkZbbVt zW(8dAee9r(zz>P!G`0)c%A9ac9m?H9{#>0(?QBCth4EbG|Dt&m2f}85!Z|7Dm65W5 zG^Ok>Xg_{ajYT3y4=_+9W5mE5{I7|XRoJ$*l-@75Yt>!O`*t}Q;( z(7T#w@fWr+z~<+uq9$KIFwVPvfhZRfm-8)-SXuxBm&NWBa9sSjnLR>F+dgAJ@_h>< zrTEhOyR;F8$&j>Rk>)L$X9r7)o0qpJ&&>%IWEXg;%}PtHEUW6ePq&&b&GPf?aMI^Y zxrlyv?97?R4xK*T$o4-Z@?|WXK6uE~sY3=&k0}Zg7V~TwIGJrJ_KWPxSw-^k!-6&$ z!IhSDtkW}PqM|HR)U={3*H3v#ik)TkKHa)^X;u)DHGO6Gf=1FQtE%oG%ggMHY5)GG zXXXw(qkZ}QRlWAb4zW+qJ?8W_;Yh1}_Djmco$A@wWzUipm2$|z&KmxBbaFnf_G?4; z$UdoF;h>DH;li>~3n~~Sht2cSud|9$(ywGVH&aTlioPW;e?KWzx5}5JJrlP4pQa%< zEHkCyx()J*=v(!tVWww?JTW>F$iwdslP!$?M=UKR_jT|Eyi(4NmuOx0Ey2Uxo`WN$ zH79;pQpFk;$qF~+V%J%rRI`PNA%9<`AnV;84R`X>7$I(Ep_gBQyqd-ND2kLuCmC^)mlHpqJ2vq6C%D;ysg>ie^! zojE~?&28r(e*yfW<+yC=W}%?m%DjYQt6sg5^4aH_Q%7BM&8SnaF%FR7=%(nA zJ*z8v^r)yteL+5J*XibgL|OL|4@U|ppB*G#xRU|VL%IR8e@BUJ$&Oe`` z-jmynK5SHu^JzXhM~U`{pPP+E*GL!kDYT8~P}t}vWOzU)`OB^@iBzf$ z-+YsN|K^@ww-BS_H9Hq2TaLy?YlOo!V#M0c__!(b8ZV6|seyxrMS}Q|HEx^Stkg$R zZrC=<%PP8t?Pe&6Oi6!JwwBR9zWg%!hpd(-Oc;McQa*lMzG>6)gU_76wj|&6`ye54 zc;pbWWmEWVA*@-1Cf4kqOMY9|rUTGeStF2LYrHiuJJ_Ni7|IU$PoUJE%3gYoDrwM5 zcHCIk8Dh`i3yQFHavjxK+lOXDiAyZ=t&WOOlZeQe|%Cu2jAK#5j;}rc~YQ< z7|aXiHqH0)=w=nCJ}wHnhx5_YWOSsp>(|Ti(bJ>PN5?11!905V_D0deTy8yeDEd))U+%=@N>#Tzr#hgT`Oi<^9*yzrI4^FEd|0EEOLbi*)pa}_ z9lRW^b!x|`FjAE`RO2A2ox~$I-e`(}!y*MaDB*(qCWSeLiOy73T4#!yiewXHs(aLI z$ZwCEHS4(J&Obj<-WJ-AUGJXDz1MRS{qtBqD_69oegTaEn&vc8vR|yAlY8Wz@1>6f zRHd?HWv-PeF?UAq*-xHtxY1XYJS>qIl*e1tkY((E z6(cBwM|Hj9pC;MCq^^i=pevFo7tYCZ=bk)#?%brb-+IBYVY6lp8+L)`BefgjV?=5f zdPjP&kHpfAwnIA>-dl(}^8lFK%gK0qU`0g*+c*6YB4dNG^0KP#dGr|J9#W<>Mt9vZ zdBUx?PMCblf<7Hv@4Ij7j(rlz{pp-m42Av~McK9`j49+9qT zsZL65%46hzYRVlqBup6_BW7vO4+=$}NH6>-vOlK4lNfz$Gz|@OA#(C^>U$ic7hT=0 zs=Rxt=`^#QOg6kf`a-kQkC$@tu~{-p7D##NQ3)FwOP{bh;sR^0oqfVdX!KCFJbrk~ zOKVfnOxzd=>gdeXwg!1zb{Y9|0~J)Z;YN*EdOmO|1=`P4`=Av0#?x0EZZACB(e{?xap6B zJ)Y#%BRaGG{5Q><|6Iy_7!7fG#*Xp17%jfB;eCv{udn^#ho7TQC$g^=Q|4v&U!qsY zsZ@Xb^3N*r^BYGmbbC!Rkbrl=@}TN}LfHmnX9VB~rT~>1g<;S3VYc z%z=K$D1Ly>v1U{Lv<0t3|4j$nMDIiEtPvtBE7e1T!Rks3(^@ufw#VGM5<{?ZjOS@! ziy##Z>FXH|oG0f+KiTqW^aFXL?0?5Gvz?k%NQ>sHGuVu@Y z#Vw1A3gbh=V4b019t{n<^Ux4&vC5SH%`!i_?dz{FIz+@{^z`YcMIU`kR{oXb22WkL zYSneUM~vtx1;YOSzs$SJ9QUNW|GqVz_i1CI>;I9JOOo>b%r&c4U4y)LPtJSR<9@lf z68A&`tq{?PJ&ULr?k$2@f#4*xb8gloDpl=T^jz|S0C7iv3j{_YrjzamJ#lt$j@L;3 z%X5cC^2*D?rSOQBahJ%h;}XrtC91<-^H3eG?&kGXx-_e=ukxA9lhJM8Y$f+-D#uQn zKK8Wet`_U0JnPutwjEMloYZwuk6icoU$QRuZPrnY zj0`W7!K^CSt&-{q#yMvHmNKhfBEwmT17;yo9~7rQyXec+zTp8W|gqp)STq-AAcW!y@28;j&P(w-X4Ix(e}>$IFI7F+uL z=XlGRI?r`b+y-4>gS>c86~F!H&34rYJQecL^HkU}ClSZ%A_opz$%9{=8eGcMyd??< z<>1GNuyTxN7*D1Y*b?C(NZQKsch_Cx z<@rA)QhAl!7_B6qiKUg}K>1?O%A6(vMIixBZL^q3Y|BWoSo{yvXP3kI&lJ^Yg!JVH zIcDXgnGO>Gwu7NdpN#$0XznGaP3_w$kxIGY;tQYdbbi~JgJxt(YrmDbO_S4_A4{u+ z+1VtE#=qyJdVi&qy4MBvjoGvymQLHT=qE@MZW#ZIZ&dy}r4^B)!j^Hn>Ni#g285m%?evIkC$7ZPPt)n7vm3)XZxzx~CB&4_j=mCK5vh~P!B4kaazr$>S<3-Xai zG(zz#mgqWJOe?p#NBBAPj`Lg+IYjmfc>3Mdso`m>18O-{q>}oxwsYNxxxdTyeU7|U zc8IP@WUQ#;lXEVfF=%Gn;eM{jAK#>WxkJ1*H7wf7M!cw?c{T>A%`pZ*gu|+?qV~Y- zkD*Y%!bsgex%{VLd}IsRVrd6>dYk%=Mu)k}fB>GEiHgj1 zW6x*sh~IkxRo8cUTp5;k#i?{3JOD64ZCX}V))xP-EUWUKP+2;4wnip;A4$VB)o|l` z_~MI3_ZxjupEmm}zHoG()5rB{)qUL5VQ1}Io+ua9y6dU)J9Ip>bLUfU?a<-i=$eUR zZfiZdd{PfsZug?J^070NPwQ6omXG(R)bgpU&m?Y2`_9GFy1s+G;z2(+y>#|?LQ>(> z>nYgsGp%eIRg}leC9qfJLhW3h*Un|C{?YQf#WNfApcA*Yc-T6k(hm)ro(~R^#n`(^b8;%cvJgeIbuvC*d!Z)RoOovZ3?@24_YK7^+}u7 z%L&yvfE(<1cZWKTIl&z6nd!7n59SfWx%rtKuyz%77j94Znp{;BJsK+r$-dEDXp>E> zqRYb^;=z#vFpq?%jDujUrfxhSTnpSYp-$i!TeBHOXs| zhtnHVJC@UwCch-sPbmC9w;Vq~9xlk0xz**W(y6gmY&EK|@GScN57B>qSavh7Mav%> zZTZkqO?Jsmtp>SfZI8cSknh#DHa?8D*P@Ty|DUkYWXG<*XJV0IydL0<5%LGJbE77B zXkh-9o{hHpA9m2|wR2_t&-q2S%k!g8CG31dm3*`)df48q+bi|Dkm1<6J?GyISkl{v zyX79Aq5uNOear4^=_Fh3WcN)6~l?}ldXtdUr# zX!W!PB2G^&O=~(HQ;_j4ef=lYJ(k-X|BU2|Q((QYFgsXSQCN|ei@BVZ*)9FfORBfh zQmUMqoIC`UYw{b(hKG#TO}IIFM1EtR9v;_{Jg!aaIzAUn8;7NvFn`a(>4YJ7&aP)*oodcSbw=6N?pAed zx*?L&sxquxS%wj*OLnEl5>?|)!o~C7HD{-}W!>6|b$&_JxpVS)$xc`QiJ0R{x>xc! zb*s03GpgQX>guXAZ{3rr_hOKrF!evj*EifxwRPNe<70nocR`Y^DK67Sd=be>8vVDD zIuNsU7Yrc%2!Kl~BI9kRos|>_py;O_iSeiIiJ2vCJZYU};!NRUZpM!(HgKx9Iy3WG zZ1C8*$v1?>5rwB9Lq&PGw5UySo3vmOhaCy^$tt1480dR~EKH}4#zuVkQ(pJSm&dYy=Eoh=|z>5*}=*-m2Fy8mzJco&Dm)-Pj6WEA}EsZ_MJL& zcqGNw^{P6-5^MLHWbZ_*MD&u6@@TxMZnUI_3gPUExz@t&eN zdr^<0^#*s;^%-h({c)Z@rY2WxjH#^Mi;{e;rRW4-d(ii_A}m#?#!`hUDRqzTvF?@C zzYvQR3L`~z{v4!sfdB0KTKCwqF1C^(OK6#erDw4fsp(y^?D(UPT&+jEv+(cFCr{gV zPv^&qHtuNDn@*&G9Br?!5%;s?YeahOF$qDmRCX}tW=SWrn`H-Ux^!&cwsl2WX(*+s zN`E}0H&q#9hz-Z85=~d_xV?#5r8Hz&wW-0yLEz&3haju0tIKk13=@xO1rr&eSxs^ci69&Yj+bKvozgA*MdUK&b-vo& z-kW@OJG#C%e;QbZ>(kqj7jVa>1G4kPEHmUU15nI-hYh$AJL(l@&nZbJGyItI%C%j zYUMvS*Uo=V_LrOfqeb+#Xmd~yy-mIsZI5NjcmtK=fPrOr36}CxAa3Ctb@y|WEwK&& zOCj*KXMoTl3b{XocD_5Jl$4%KKZ}OJ63kqlXtnrj>`{d?( zcZ+#)xF4EIFTH^&etJ8K3zl<&`JJ@Z)R zM399-G}(B45p6ss>->{4ABi!4CEiTO^R8oM$CP{r_AcMln~JSe-?I&5IKCpK0b>{y z@wF+iN$qnlj~w1$DS9TLM~yv#?1Uxu|EC!&-*U~m|7$XYx2TwR*SZHhL_>BPLx!YXf5|-1mllG1`jQj(OFXVk~wO|9vUY5&i+&T zPFufCh8n1oo-OvWJw*#2Yfr;<+f(QNsy$WQp2mIglz3}Oo!U@(!-zp=EY&sCgcimR zpt(gu#w%YJ3(HlSravb1|4Fu_xMufP&RXnfSS&h4u^x^piaQcdO>Xwb6T{&qY^7A6|Z+@pSZwH~*3! z$bs@m`N57QQS7RNI)OC0M`G8`HqsxM28!^qgX~`>t$qk=(0W{agJt)zZ|Hr8^6RA({DTe(fUlXo_XUE?wVw z^s-Bbw7YmuAiRO=Ck`!lN~sh^Fm^~u77fXyjW?X=F6oDRgVU@7YPc|ID})9Lv$UWa z-py-PkV9`CG?MvyE?IfvkH<)TbJtV4H9x$0WcH8-OL_UYGnE@%B+ZVKMxeOSwY{cx zs_&ES#rhTPOAU=)78e?}x=G8WVH%iRtD8xBrOE5nOmHanMFE9w8BaE6@9FGFO?@Zg zEi&z#mf?I8D}^G#oMyRfVR{>{0SlwL$QlQ$9W7e@>W>);oy*EXhe|dc)agh5~?f@wJvEL zGsvDE7|C7#y|c-#d;Q+=9__I(se_F6#k0pO&rdF0UV;~$$xmqWfX*1=Snw1)0cnVh z=t&}T82k;V*PUt4q@;%eA}wj>&_U?x*6H1sO;9vX+(W&?X? zmz|#^_8-`jY0Q%oyAn|U)N6aawxGS}EViY#zL7Mq5qRdK^-X=>DLuy-8shykUdP0X zt;sJ=HF%*2HayGlBrxe69BPvrzLeKI=XL1Np>v1MojS&wX?kB`4<(V-OslQ4@!9^o@gORQB{shh0(XmYg+A6=PtJjJv-{k!S__NrUl`uYDuq+ zb4RbNI!j9Iup@Hvni|f;P0<}*m+tr>M@s9ob-RkA{R%IOPVCt?spB_&;@~_vC%*ta z!r{?fa_BVqxjfG6=Vkv9efikvlHZn(+*uj)dx0&YyNw^rCk*Nwu8M9$f~A;)db`T& zt9FajpnGOwlQ;@+Dr6L)VKb30IRX`TA`KQ$$&0bmS5>)#7xi?)uA^|$DLwfEwMs<3PueLuQ6`lA+qM7u@TqI68c@=M8+lgn418QvV2Zc0Gq+z?+YqA@hs#i<&pXwe!MN zH5vonTps9h(vb@kcR@a7hVl>|#RJ=Ru}nl?scm!Q%z@HapU8|=O(WVwcI>OKKWUmA z{m<5TuDtP~b9R7U{LRH+d&=^BxM=LjQi*}OG_iY`Mw@Bk5t3?f8tP+JCRXy%-z{?v zmc(Osnv4exi?qnglOnGOFMPEqXqG4N+~gGt^|lSne2 z+smRY;%P1r*nb0y#qsO%=Bb&|E(UrI#PcBW8eX4?X$qvFger$(d6~@m*JK3o$expu z(pIlMDe#X}TNA8*l{`FxRYS=O&6HPc%$&YIzmz{m5qj~`xGJ8;A{qr1QElyy-BwK0 zT=WN1+DEUt4#0|VDtvO*=5X4*aMoC#zoGOFP3q`pPWFzrvF^RvJwK@(FQ7RIVZw&q z?y0Fs_N&*wDtJUc8%k+y7Yci3D)`#=v3Mt*Guey%=S+#X^@yc~=w=5BVcdLlNR0l4 z@qgf@HbiFV&*e$hCZ|A68wX?4GXcAQD8HrbnCU3*>E$0p8hRL@Swp#L_0nej{vPC}S2<{Smw%qu^UN`P-B)uzH@tT6A_>bOp84b6f6TnVyB6>1QI}R)dRF4eNBujLo81yVoh3RV?1ph7IxXol@f1D8 zL_EqlOJsSkS5OBOGk@{_FSAC)=FpR8k-2YXx5OBX_D808jDH8rb0h^~68$3!69eAO zf5s2u-PRMvHPb23vX`GG4a()4( z+fv3_^W=#s|Ff-$lG$EAB`^ouw!kL&c45RBG~QakZC~Hu$r5 zB(gNFp1k-64~v9o!$17kSJI}<@myu6c}Q~iB%IW_O3q|a`W}DD4y@me?bIAof4l*o zrrOY0L?V5VdJ0fu0ZZm$*n5vwG~bx0O0TD`$p{GaKoa+Fj{CO|=O3|@UcGA%=W$(o z65j4~gzKDMPg3G0CueivID2(C-ar)$xdzqWXi^#N!f_z$~qf*CY&kI zJh7!GQth=znCB;dIwv-{A8$001A7@7jg-m#1`7jOA}`_{+v5IHr&eV4nc4yI-fSWt z`lX~kEb>vi&feE+S3_b*j5m-ed^ z{pu-oaGvh#-?W8>NxLNoV>Sk zvVr4uxl8PvM3G>=D6t=8hLfp?G#hVIu=uiurS39kKKe>jM(GO?^aoPLvjOr5VLee3}*a!L&z9-SZIh z_wJN*N_@gA*+9PAqDzO>b+mC?0}sv0v;b<7b#`q{pUg^mtfbFd`F@%8DSUxe77mIG z#Ls<5bpG8jS$lZA_)N$JI-Mh=TUC@+)+$q9Ae{pOno z<%sBAc<%gvB8^?K{p5wwMfIhTd~KEQYk9O*h&x!q!T!h&mY8I2a@kw(M5HBA^icTs zU8R}zbd_pX#kxxLY^&Yqr>#<4&MPRp#F9jvh%DR5qA93kf5gv+SYxOL*dy@@HzknP z!^R(ov6Ha5q^<4InNFr98Th3b`ke@b*z!}J;pv`H|L;X4`O2=7>i?ODL<>;6v7R4_ zpco3;g8=nk3K4YemD(co#hy{YpgSqnFa!~n3kK@z(9jBb&pb)fal}K+&M*|ng*amr-Lb0I{!frFIs_<4;3Et02ZH#Dh>*VfNdSg^? zcdTyHB#&>XgQk-ktH8#1Z;kxvU-T%bwT48G2r|&&7LQNE3}` zu}?1f)__Pq>;#XAb98exw(g`ixf|-J`zvqSzsoVOQ`s+4gcs&AdAuqJ^ z@51Fd68nncVNaX>;sq9LGHC~9V~2Og4LGxc8Z&*aBWA@j=5p7z-ZRvdjK~<*&=})%1EzANjm{2mFmue>uvy?RHoSf zHhBwRp?2Ryei(QqZimJgZ;>6SRoNFm9ujb|W#(*eSA2UG?FMWfJBa+)+~hj6vSI|3)!p>|E(oq!W|a6eAl3z+*1)36sX z`9oOt#c$R~SIhp55dide0jBLIX#+`(-6yfjtZlnz2hc^NJkd)bnO)y+A`wrdg=xKF zm%whq|NaWJGjKc+d-~!71g_IIX=$ z^oz{nFu)8jh=8r)#_EQE zQCUP*7c?$03qd0q*DS`BXv{Mv#>6NpVBDjL(JWs!v&Y1RB$}8QO$-Rrm+xQa+~wS* zXL``Q_kQ2+dp-g)eQ%$tI(6#QsZ;f@6)0H@M1QOqB%Wo|;1YN)f*mZg{^YQj!IBJq z5RMv5ehT(CcJq(+gzHOL`uV0EmIqqtvMeqjMyC9*3@=Bo_^C zkcL<^|C3AOUupcaU(2QXjmze~Xg+ogyV9(>R()>pCv$hrHNU3aIrjncg8gf5PR4a} z&{YH^a_BLI>4lp_($x~(qaC7X zZs=~*Pj<8OgiJPnlBljy6v+`FL{JaKE(**-m*PF&b1+|Anxh|+fL=6OJMo`bG&d<5 z^T`Gf$VG1BV`d$~i1cV2^|3bbwq~Ik!QM(_4Y8gj^0|%(NK;4bglyws!iU46uaj8G z`cPL~hGI$pY6UjnV(|5Ghp)}~O5&>)xnoGN0Zf^tPTj~^S5eO6jXKC`MR5nNR)8n; zJ*wlg5cN_NRNvJ}lNI382ZEy4xqSY6pC9tXWz?NH2j@gnE(4!i9X_wgS2lvrx)iQt ztC$+I`5c=>M+cR~e2hjD(quZEBGu0Su*|CU63r^dBf(M3fjxERmT+LabL&APVS2yI z^PYKr$WxbRtS~>?2+Sbn7i%s4Er;(*^GVKk27KFkI|fYv!g!W!hQo4a@L3Pb{2k)B zz82Rkf?s5E*#;!(=hg@Op)Ym0-ThTB%!kWsq$+^zq0;({b#OjY%Ezet7*nc%Ib$=4 zMpb^bq*W>z7-x-k1Rs?bt7B<|IR~Vj6x{c#a9rRAKJK#h?iVcFK#8Am4tAibd=7jV zz~Un$zQ;uzJyC_KGGd5lWr!c5_yLIp6dwBDv8@d)2Mg@>HdhXNK4szhNpg6~hV~oo zF5jvxCa(p>$g+n3q`6W&1UpVq{4Sq^-*NB28jSPKl9^qsHw@gjG=erDf?J71oIVwkW)ab*lv zroG#ft$~`Zo^K#EZH-PUixUI6{gGs&q_Gbe^9Ik*4jp{b_GbstvwQ!uIJ?pPYcu)n zX_5biZ#KNyEtK{M&l-tuE2rX%tEm*QKj%2^K+~lzHJx3l+P;4LH+9Ne%pQ$xE!@zu zdR*0*R7^kd5_jy=p8fGWKx7}O&0I=8HR6$miTmt0{I+%CKD*s!k(Ni`nvSwC!7Z|9 zl?Wcd0VyOU!g^?FK9fWqLaH*6NoH*6#68lH)zOg@bNj{!Q!@B~=lUA9myQ z%9t0w@>lakHtJu$+O3;MYfE>T=b8m%a?ksx$sS-8udxsPquE&h={wE4kkkF7`Cl(T z!HzUd^PlENe!QRklFbGV$glR^Z$AHhjLm6|jRno!%2D~*NY9Du8int8TzO3r6${wo zj41M+bzNP7Trrd+7>1O(->r0av{$E6l)vAW;>8)6TttzpOhv;$!PG!@N_Of1F;)|e zjs4uo=8@*le6*WKI~*21W`2>~Q_hIZ3iZZY_nUwAvfy%g%dPzf5uYDe1hxcU@u{&N zC=-+=`9)M_mPHq~F2>;A#R2)EGezHwusI6n1XdCUhhU7WEw9?-bA_@C2BZWk&BhHA z;;Uteh^|avy_AiJ+@c|8tCrVfijvGoCRH1K*gT@=w_h4+@~+;yb{?Df%{41pW{g^Q z2Q$_uuD*T9c|2_9=eE|H_iE9(o6KLp5fD0v*iI|z$Ue1w4A`S%Al27RhA$(!Us3ek ztD3wgU6_7SgX36~G8IvpwtR$i7-;nOcxQ&C|Z&k)ATZG-8M6e?seUcl$hG__{dfj($ z8H8S$D1si!PC`@=5wt2hu5H{%wMxo%Dos#>S6R`+lH{YEBHhznSGd611KO3#=H5RM z3oj@iSSbHiKo^OzWz>W1Eb775>&nL#3+|h+5>b;Aco&s&`MNB`lWoreTYseDL71sD zvFtmV()3G`$(|(7+PCe|CeSoS`SkWSut4^R>NxvgSPW`a7@T<6nMiaQrT>WRN_)3# zLV}F-5`BorG)hmjB@`~11}>Q~+3qe}IJ>ebZI#&xStSbSX*XA^BrzE^Zq~9bt7}1H zyD~R_bU+12JEF^=D7bV8VkPcKDSLV5ceI?D8e&e4hRaFtb-iz>?+LUW}L>VQ@3JS@Zs8kt$ z<2+7b<}MUIMl=-}Iu-~FrR`JQBU}usg^b1!j6H{H%6f))G8#oH2t7_04tN@?ohhTJ zznqo#f~)2>)<&ge21oZ$IJ$@IcL|>>+m(yx`XcjXbKbuY=GjT!;>Z4tra0Yyg-`hc z%f9v7y%967Z7_e;kX&QF*6ey)_nFWC;Im(}eUU#t!j9bc@LxW5r7ertEXblq`OJ3o zQs5bt#Qh``Hzf=`u^Fi@)Mj%{S(5HQ;s3**Gz@J8qya%hB5Vbs)dIz8N@?%lX?l^n zr01xR2trI4zh~^2?k@7B({QO#G=*D~430g-cCNT&Y?m}PYlX%5<3osO^S5T0x3g{K zDhUcssi{K_#RPTS(ZVl|V?_2Nn4oyJh> z5fi;hE$=i+2w;|XYNc>C!Hy390#7DVRqoxM6M?uY#`m<7h^pIjRS8~E9q?s+QuBJV z(I1#c_Plmyn9|6yyKeu6xvcM-t54_ttg*iQnp*=ZxS-2QThNZzJc5;Nu9aNp;g@;rr+&ZrMdZ1) z0n7HyBG!+hky9a$WHPqV5l9jmos6$9W(>=m>7a!`bD5N+S>nrin#R)Jb7rH=va3t; zP%S{gj-}kR;Cz&qHXoWNcr=1ZiTmC2;U^|B+2@=w=d!Z+wt{xp(#kYwQspOR$b%c$e%S#i3M zrXxjQ9;7$DQuMwJlqBEOD=~a?EY3@iEyu+B1#mYo4LM9ht}};2{Q*w{U1yI#{Dr^M zVPkmV7d~Uv2iyC_Cca<%+uASM!JV^Th##c=q910Pc8oCpOjTpc%21~ngSU;@SQ&M9 z3p&048@HraZE3D__6p?ti8txn6*Uq=;ziIs^uLE$$nsg}$-&9#>z8 zm}}jG&G1u_xYa$?xcm6En3qoFo{_8vQu^33=s%{Qh+PUUh47bbajvjT()*^GP`TA)JdXrH7NF+NdsY1Hg$sR`S9waE3tAn zaEMKE;>YmkwFvI{hI=938F&_VgVA_&wO4*{PD&}IHpIzjAR8f+GD#BZC9IQ~yZ`hD z^DV}@*ayy9f112l&VBHWKRLR9;!({=0R1XyAf}YWO>0tJ z1DYeN=cA<>g_Nj;rZ2nJi3{-y@!S+UOT@@#va!qWA?w)YbrcS|cEkX8t!Zd$1 z@wCdiV;Yv-W^Z1*Z#()McdQ0D>rxgydZTtTHZq|{W_27Tkq}v>a{Ao+iR3H@LLQ~q z!&>Ka6Ou(oQVY?dol5jZ+0WqD9G+ZIEuN*lD>SjypIzb7OtOwVWUiEFn{^Jpq1EvH zc1P@&oW^>ZYutrv$6#)8XHVDaAlBY+AvRjbSxuzm)R$2{iIu178@nS@tMV3Kvsjlc zI8MX|*{9EYHChqvnS-dby9{4{^GoLQ>sD1XR~-M%%kT+;ZIt_v_y+!2u8hx*1^SSR z@ZB1GqA!aQa9Ar>Jr0A7N6IO)L(CwvW>BONB)&2Df>1S5G)lC1_YBf7F*+Y-|E}cy z7^i{yanQlN6{-UM*%|}DG04%5M%Qu3mX)d!&s3 ze8BxW(I2Rf)UX0yWUD|FnTGb_Au1)iBhNIAtt$5-HRLiYSA3-yeYBg??ID9JJtrgh z1piE;Cv;i(GaxD7`ytor3?41=yzI@3d}5I*9w16fPU}9?rE>5ZC#~D1(@hShx!060LuXF1WZI-G+u49_KrX2b zHmOL_R&^rQRRHHRYu@cg9E&*8H15k?()YdUOXGGK2utGzA72C?sQADFpe~LZ^JNXS z)fz#MB5T{ykyhlsFl1+QGfFGV))ns9^9cw%!awrr%*gY$C%cU8ecYe7?Q-T$jg+_j zb+FXx@b%Gr8TF=&A-5Mv+7@4Jh%SOLx&W}&DpIe-Y`L5TAF^1ppQUMg4|&?hx;ze6 z`17<~9*gE_W4$Dgboe}XXJcIkY}(5^i}<#WMQfeA90s4UIdm8N0j6bN=rXr&gEuYP zWi2EvTj*utH1u3dDp{q6bqW8x4h2)OdL(FJi-~k~!Zzq=wHQPDyTB(2@d@sFsC8X8 z9ibzXI1)$rEKVTTVv|5vjhnqB`2?iO;)p)3*z@GV^vy?a%tYUO=^Zkq6YyJnZ3604 zWH9lv^I8dS@Mqnv0h?=cTN?sAJ#9U$fHKGcjmX)$s9C1iSjGPi*XU5Y&YhjY+xK3& zdd})oXO5kG?3bO@u8;T;|N# z=#V)Z(_L)N=o9apbrln1LYRL?j$+Dsiajo*jz(O88SBMe^N1#F*cXEHg_^zgTwtyO zUi&Ie_lRK=)Nvox|CAtHgn-8s2aJtE9q& zM_kGWkFyFBB+8w_gdyULt-?jcc?65daYKD4kz#JBrYtKgiMR53q)xz3=aa3Yq42Pg ze4C>VvtyO0hFl=$#9tN{CXb)_(a@1#RgjAqIS6MqA;cbfLyN|oeQ~H)kd5Mdnt!pf z%zjB800wLnH4%p|AT_Rc*bbO5#6rTG*Gt=`ns<7x?mIEyUlx`~Eml2!>ow)8ke%=~ zNp_FslWkd_?8f+H7Z;|dxahdBWzh}shL{=oVM}t>J3hfRlGxmK;EvD&w!|kiw{%hR zaV4Sc&4swTUa>h1z{$?*%9jgEEZ>*pHdc8qUxv>0aDQdB<7y$PT>_nqxhE{_w$QsW zkKT<7Xwp#cpcL7sc$W2bM&bgrhJfa^3eEF2uy@2r4O~6jr%#Oj;z`NtmW284hl@Jk zUXcd-*Xzm`Ay?sx3%ULwUq#*P6G+%%=;|He<%YDR`<#u-yR?z$$i0~|?#&1hmwQCq zFTW|P-cYaNCfMF6eG=ybDU^N5$K?XI2Bf7<8tZLhU$VIUHJs8}Koa+!6N?`Uyz+Kb z*f}HU%GV)p*LIFo{zhHYTeYMz8nAA%yeC((BzD9kTsyD86BU+H7nk@H&%3h7hkQ9- z{5?zP&g(v@cXo6LMHemALk(@!aLVqhCJi^Y@7`7Xz~Ytu-62(`I@w?A>>~92m3$d> zu}^yJNPk5!*$=DilRd9`L_fs4`QoqHk`Gt+HtJ)qDE?0Nj>=aKBXoaJt8{vjRQ_GH z*g?u8@?ax-ZeOf3mFO(~obb?_ngMIl1z3|#ro^XCv9>_!9G=9k_B=LP=-!y_R;jdc4JUk9hDny_Xt%v zx7WRVn3YxT^q|Uxook0wI_Z#0_svVJyMadK{_SM8=!i=etJ~HZ%R-5@EMj6}8B$q^ zdm`fJ0C@u`{z5+RtQAfZukg*<`f|8P9LL*kbMFY4@DAi#2r6Ke&Oy>?P?Dc~_V>sL zK07~us;A?es3{HkTFRiul@RY4fdADLEv2EKGsA_4db+ugN>%YFeX<`}``V{aXRhJy zuSq#steM7`9@SJ;TS0eAXQGvZ`*ltEdi;`q4wRsE@<)K+OjY4FNU6(IHKm$>JXC2^ z^1>iwmS{%FpCn|&B4gXRw>U&mJ?kgbeXj?5pfsOW`&?FT)0jbtQ& zHs5GiLoJ1EF=T4uF{C_MeQGMpW9fKWkLtL-;uMxy6jC#!Vz>1~fO*cENe}--=3oKeUhV`H3oH^QZ-Z6p^O2R+nfb z`Xrhn7ERRDn8r3=rS=^plzoq&U5C2=A>faGkAElc$;9z}=xd{@7L}N%t7dg2_2bx} z;-0L-`rSR190l{cofq~+G9_k)-`$R09uRUx_K5GsDl@Y%V(^M(cHsT){Y&uOPoXA31M=!g+sl~BQ^2Ucwh^7%G^`JJ8MJ^< z2;(RsV%ca^sBOU$Ox+@X!f&&ZxKg!*ZqGmJq@k6WY!{cYClQJWvVqKKJ*y}QQuHmr z+0@XdYal<_JJ(EC*UOVlU~5HYdIBpp)7o+7!r4R5bTQ~m=<)jDzD1S;_pd4k6`ocv<7>25IV3*=8K(eH z(v%wLhc*vsz-+F|IVHBLu(^Dpk|9l1>a|8K_|kv}A?!DdlOF}2Ncnpx{5{F&odEDX z*Av8t)eoXRfb2dtP09tTqmebrN%1bcCe(0i_>AnmODvV1RTGhtowtb`knB8B_Zuf$ zi?1LzX-u`&&?eAPrksL`F{~MoLpg^{SSGZJHb-={m-8>8dit$~Im9yat)d!kN6U1i zCm`#OglA8olaoh8c^as3ZE{icwf)CHKD#Wtfq+528KABjn={1DI$)vX(|1qyqT_R} zC0Kmu>@CpQixW43ycFf!kj{>#+if(R)6OG000bz4vRFfaMiNSZifT zo*E*Hzr%+pOcbz-!k~nSh9HLUorR~=jeIRtD%dAnT?{Oo5S^5FbsG4W9-Vy0#aEVn zsfi+BUPjXnz&Uu$lq?PJSz*ig?`RdknsF`D&;#D*VyU=|RJ7}mLqhg$j`+5Wz4@;p z4&1MGf6u?4f5=yv-!HJnp&|>^fh+j92`*qIng0_Hj=X~z< z`#6O(<$b&+q^RR(k@~(kei&0#kn5tSxDJ#nhwVV?UKG$;cS99G<3ysLOED$>uw~Y7 zNs?7ulI40ipD)8AF6SJU^_01ArFF$X(v;8%X>8oZYk#xGp0NXfDDK#ZfIuJIq09Nu z;+HfsRL1f&b!$|^iQ8*`!yKOF1nK#}9*jZQ>IfQUfar3pz@v7i;%KVouw4niSBJ8= zgHyU7TW0^-QK59TZnGY-h{*b)-jS%eumDWmkDfE_V6AKm-PMULEeH_^YH%NHYQ6Bb!8le7P)Vz7Pa&qxsD)IS2k-A)$mBO z-uXy~)%u)ht*@vcjjBjj0It>%Zp;y`H_B@K1j2pOA5!Hshhy|X-;CW3-Ai{?SC)fp zTixrB@o16kldb!SpjdoN1lLqxO$c)1WY4iRTEqfuXeR5=jAEk&CY!Mv#Ln}EB8gNp z0J10apb~ijID0BNWuLG$@Fol$7>ZgYW9sNpSS$KGY;Kgx;r}EeKCyB+M&SmE`XH~v z4nx_L>tbjpB&m)_2Way@73H>+(tVwUAf~nhzk{5p=a}z{#Ve2zx9Y{8<5J`gLxyyy zZ$z(&#$00#{i3SN;VE)S#w|%|q2UoXB4<>Z3&#gTA$IhQgv^FY#1QhH;>i1-A@a_) z7Lzx9;+1!cUo4TRY|4bVv;K!H*#)Ex(A!dkUGy=(tWp2c+~kq7y}ZKZ?1rI?Ddxfx zf^v34PGORk<4c}%#fOTYD}4G!XFuQJ!g<1f^5ERM+h{eK+jx#1_ToH%qlfRTW&0kU z1D8uz#`!%bX?WkG$XSf?PV^6On-xcXCuNN|2CEihKsT@%Am|`1=)C&@A6$G&=Bk?S z*KytU?x*W(aaJAI z&i#Fu9D#q_#r!7~;2a_oo7o75SQVhul{#Qo^w@vH&~iQz-=unsCZ!AddjbKyB9STLViG z%1aoos*iSxk4iLBnAA&8VKj9&#@L15TDsXmJtZk#I*xMTcu%TuVY-(Eyerih=W)gA zMiu@VB@GYNkIJJ&MPsTGQ$<6oUQT!C!<*Jvaz{LabZSQaAn;-LrhN~Rht{MeB1H(+ zOOe`RwWqb`{{5z*iDj8Yg9z0QM}z2hah_A-N->s#u+?Jn>GQt3-ZE3h(-uXmVj!99fEfNLvA4DyEpoS1r~hO(w2*q+g=8 zXc*W>dRw?w@-<2O1tKVr>fK%bW~}gcY#Sx|;;t1j>0BHKIu3Pp zCY9KDSES`l@Vaw6hh@30*fW?0y7Ug|$R;x;#|R~ker<&cy-cV*QXJdzO+@Ydhn(7T zieq@gfa`JNcoKi0#G4Ob{JB89xYylAhw73i9>lCMdz zSELDBTJ(=S0B5(sMsCSe^p5?wSf4$F^@s*8NOyMHzZP41Km)g* zhD{&#aqQS!u;Kk;WzjNE99`lq7*6*wc#6|8*3oyXj~{G!cptxF+!g8I=b_UVZS454iEt}ARF?nuZ? z@Fpayaf9q0JAze>@FXc>Z~w=fNU+-Jo*FTxikKpPy(oud!%B zNCbEzGAZ6}G(IvqoX{}a?UGT5SRrCBA+JcGVw{mqM@{_o79CA=<4Ap5xMb9M)`W?+ zGG2N}H2%6oA63j3E7ogV+6GVY#EiWZvNHnFV?pN;4xLBmk03gw{xWvA7|RTX%q9`+ z?Gxi8n8YZ@jJ0Q7g7*!0W5-^K9htf@WSm=KOB{NS&oA`#i6JC2-XO*zSaf;u3b~fo z%!GA&@lnn)-pH}`YnSrDE4&e8FXd3n@K`d@e~CjskXNOCFDN4P(oXGK{G^AqZSj$9 z_+rSq>XN;0mmEv>5`A@`Zwo)`NAIv}&Fq+Rqo+O)3eyR?4XZ@Z{~^*{?GLTU@>qmc zJyn7zC?HdTB0)dDR-It4{|%}PTuqgM^d6iRuatn0UbINC*gJ?P4^i{ylkl)Le_2=J z=9$6ZrwB58rLR6xyy{QP!?Vsj{L8vWl;@!vq8Q_d8p##D4-@W();x^yOVyGM!EyNn zDx%Nh{V$-}Z>U+A=+rZWPk3PBD5xJ^$XC(?xIt&_C}{$Y3{HR@eI9xWq$srGC-7d< z4a)x$=am21GhgJ-+zzIcBl3s2{h)C2gc3*9&DJg%+_Sm+;42&SsfWP>)9PAR2@1dE z5o+&zpJO+6Qk4(mZuh%Zz2jxV#~r4xQ!5V0Xjd2qTsmUcp;jpCpj3qWXbu!X+0g8C zOi5=mOg-9VXYl(x%I#&RL+a0jedhnieGj^YP<+-~7 zExGs1XU!MCa2YH6!H2J4DB`Iv@Q)43quRPEqEGPht8wEr;s6&t*$0|qsf*4y@=mg>5XB8mr3z3KNAsj=6%>a zy5e6pFI4Yb1?;!J`~;-fP&)8tN^wPcgt697I$(HeQZ;Eo6;1(uDN;~5j3D#(l!WV* zCVP?M%f;sn*KC%|`mK43y@z=#J`OH>tZ}-naiZ^O9OQjP0Y-$(JoHCOCZhOqdArtreqD*P#d+?nWYKMHZBo1AD2e9PM~OCMUq| z4doC34buWdv=O4LpOV6P4t^}>9axBEz)ksZzN~^p3_%lRj8IsogA#fSiWXY}LEl(f zBSJ1QOD=#Eq~AvjbOEOJq;Bs37^jQQ4 z6M;5Xp;Qpom_QrLGP~g$SEk6%3TJFIMkXn+9LjW^3yPZ;E__9Y6Sgw7$VP z`*v{NThYIU)_;|9)^c&*cNH*}YoPIX7y-w?lL?r`u$L;KbZ(V)j zhmSB@wD~Y&U*3n2uT!7<($w?JAC4P)lo{LifYxmOv+x$a&Z0rs&SQ7lN+Yfu38&1l zpnbxEa*!qG18!7ZRG2(e-B$7lc8-3?!?6hg9rEjspoQnZ8I;G2@4_CAbcp51hOpR& zAxow7WW80CgzwbOjw$cr_8m`in_>I}_jU$yDX_Ogumr?FGWEnxeYa|{) z5d)samqwj0MYbwg2kq>AEtW~3@3#yA*@2A~jeHE3O8&*PvFYf_s_SRzLytk0UORiR zUzm3qqOegIb&C(Q?;<`X=Eo}zJhOe$B0;)6BnVPgrM#7ygi40BHDv6XnvQu7a%JNi; z0ejit%p-0N$TI}4Afl4C)(xvD&S-brgY2g=4=&$_t?#Z-4$4ohz}%4)M3$|tZFWbX zN~blbx#5B)G4nbAzdG7pr_h(Lc>GMxnTsq;>v;_Hx(l|jTjY87pIw@7r~?LUOH*yO zE}O0_Pe9wa{3ZPYETo>rMWO{=Dqp>ECRiQ2$NZl8kJ>SHsmAg(C%oWU1rg=lhs<|g zN6zPb#uhJ*Cf!Y&+y6K)V{xOL&5tUJK|g?f#6)n(via$BDqUsZ^bV^8TZ$RVVc*0i z`G8uhPOeuctF`7SslwmQR|Tg&m0pxGps-Q}TU8n*nXg)Trt)I9fvThPs(V*E*m3 z6Ipcurn$MQnM*ASU&cSnUewvp>5lK00cgFF3xLqd`aWHnc}j1pAj?NxP3Uc1O_!GcQ$z=|fd@y7U&Q z?&S6mJczsj)QPz~82U17BB*Bb5YOYOm1UI`<=~ox7V{Gho%E6Tq*ICO(j%QEH(K*k zNp))0`0}9S1R2Z7jGPr{*&ZC93aotNHwwQ}p1yZv{}TL*9FVp7WH|~TI1P5lpasD) z#G(*lCTQq0=TnWnJD0dQq2C+&7MK)ci}yq1uW)h6Ss!VXWqW7ZY3Jgj`cravK&D-l zf&>47`9pf3kCJaYRoY?mhjZKMs7a;12Pf~arq9{JFmu#0+pk5mg&8Q(LDkMWd8&E2 zi!bLVIK>^(7Um_@T;l8yvc#VC?pv0=)fLq@>b?%@$b%Y|7yJ)5<&@I{7zWdOGM#Q9#37Kns;1}o$Y@Zy(lOTqLEHw^`J;AgIlZ4<7aE` zufw}lRkjk|?gr`%fF~Q%E2r}KrLdj=*XP*!wSE2gkC4pCkYCRY`ECEYtn(usRo$t$ ze&S}?rr+}HkLPJocBD3QY3x*P-LXHSF97Wu^~#C)8XU$Hlz{6B((`nUA`)!w-22C4 zvzA~xobCWPDmLZQRD8p`0=O^&OLw-n!-pjo*4y74!6%VGE3Xuh?* z6Og^C!n+~ISw~zs z&4Uoaaek29SFN4TK58zk9Nc1FCAKv&HKdpPJFR^IXB+EV;XR3cp@Z+9&H!a)l&fA% z;|TYOf*oQU#ADLFvD9ZQi$b!tv_WhL_P8RybIA^CSaI@ch7v}j`bLG~6j5Wn@Zc;K z0g6Q*!w`pkx)eB))os|W(p9nOD32VxX`DVv^_C!7fJ<~j!VD1d5;?Jmyu0$^i)oFA z)*+-td9XejjG45CrBV3`rt_f~a~-$6Ic0?_ErRcAiUXUF>@V${zUgYp)A52ofR)Zq z5zbU1u-yOzoY5`yxrUs`10%5|eCi|HJsb%gh!`L7YG)84#$_cIM2g{j6?Vk7S$F51 z%7uA2@VE_Q^H7nf9wT(2!_ozGScS62)XkyOjsg;)6hl9@G@oj0Z0v08v~?sgq>dn| z%56d6<2R@<+Do2ufur$`^6ah)w5Y(E{tMRhR=%dEy7NlM1G@dj@qpBxpAO3DU+&>x z2Vd)mILd4NFGH<$eipFyf?4hk&W|G8Q|{wyo!49Pvx&M){F-)aq@Y=Qff4iiHQp`2 z=RNb-Mq(0T8%bU*vx$$3$dH)bZft|(WjTH55#`YJb;4uk$I6xPDsZYqN;egCM0LQ3 zTknKFh(ysAgi%FxO+zM)P8b&5e$N|rt7wqJ%ai(av1o_9!7H1%rbK+NM|)d!S$*WA1w64RaT3e&Y?+tSja=bL{Rt?3akZEM&jfz1tjXZevHX z#pvDfi20~_Hv>G5E@(o{mZ)UvSJsIPqMU3|&g46BfK=pl+m?mUoI=@wg&EV`(mY{& zcmJ4vF(bqwU7fKZI4ovyO5uF)yM8lNiYy{^c7d1uH|7I&m(N+^G%lYhl*I~`r6V6Y zysQspd~#nKT3c5OX3^SuC}xYE|4L%_H^okcC9z!@TKYkJ-ws_p4W9mJrHj1hit;41 zG~3h&aHKk5&7g7|iG9i(TX%V@Yp%~oerWF;+f7<*bPaT>Q>sXX>jvtgwbdCehb}g` zi@^m_4Ax0nUBiC#%ng@(R_JowZ$J6WXD_*4sQ7EY)%GnZ9Qwh}L!}3gR-gFbR%rOj z%YJbMyJ9a_>r0-w>?c>47w_ZReu=hq?_GlxbJ&ul+FNr-(ciV#-H&3c;NP`VwtOw> zE(pd2?k^T45Nb4)z+AupU>ZoviGi-pF+BdrvNfthce|P*Ui$R9SxjD#(Z^Z!jdu*p z@GZ+T}ME5ctjW__hKg#9ZuB_bN zE@j>1;9w9o0^7{bxuNU6Dc@?R+%Pz(VJ2SW+lUE5zRggU!M+43iQ5U%$*UBejeSmj zjy+!#lP5jnH$ylZf?2x04sKH;&@#F&Duw>+U|%UzM~}c|lTB|ur+199T=){)1GG*J z45x-r(vcM9o*s%mB~B6Ce#erdhM6Tg)U{6$o$^Xy)qKK#)F_GbQ=M8ed*43Rny(zy z=7B3D9RdkV$K_(!q_ws-H@D_mb8T72L?nhc5iE=UO5*BpK$NUCKRNhQK+LQ*zgY@T z#%^}Ixsv`kbGpe|d+WBh&|0(7dD^k6X!UL8ZQF|2KJrNH5FIm$t-eYqZ+mmDx`vh@ z-SZ5&1nn}dQY28A;0E$a;Xe9|Th>MV7ryQIhrN_-lWRj{+2f|KbN0UgN?SPX1cX%;LXin19nX^t;$@AWyQ zBj47R9R*aokclttq`<8DzEXH994~|7l`^N1q+AbZ+vFQ9n|zLSzQavCnrlXk^KeC| zuYiwLbn%X{AH@bc^s~7A#3CNkJO&;%X?vU!SBKh2Jr#kaNMjJFFUdc?%F3AeOZI^c zXPu0QpwD9qIZ;JsxCMlUI6L6fY@e+LluwA$)wnWh{B5A|H^;)Y0yjnFGS+ze%oNX{U z%r;0q34CMDMk_ta`E&2jAVP-z$e4N{@~&~RWQr~$ zUa~_WF38ua7CpK?*7dl|&g!8i^!fZI=?Nm^yXpK~oQ!HXwy8 zc-Q5j>}eom5wUQl(O+?vw|dOznO%J|PG%g!yA*jC zZ)@A0WK&w|a%|zD(QQvKHWmM#&u%4r8;ajpHaIEv)Je)r))O{Ci($gCsS7_K5mA9+}Q#`N~Kj$?U>htO-jetc=dn%cRIc8hWd`q+(T**yANyMj zw;r2eqYM-kZbD0A191zsiQ^!JZsUN~P|!SK3%nwr&Evh;7XFq|>`Yvj%QZLWI&+;J z?agh?xIHDamK^469H-RGu4qSz;kbQovg`lEZZLoNvtOtm{0p07K3Ku&2 zrx8dAJ|)V4NOtsE(FmSTUezVa3)0H6j&!cHc*)*}CN|9G| z6iJH>ylGy=E)??OcNj@=@6@JDVyS#`5_Ugava%>8H%lWJsxvr^GCc{Sb)e;IxNjBmfc9r?t-@k1BmR+U( z=Ed}vR?JC!{}<;TUeYu+n9tvL9~=1Ojq$^JTfcbGnR6E=U4O6D(*0@5+WZ;qED@i; zqOo4+E*1jLLpc_o1L;uv6n!%ZB?|RNQAI2YR6z^VCyg81-8oVb z#XTn^>LXL~3CUc~t~Y=4^6$)Fup8OD&#^Tp9kOz1Ut8Cw&sf}l#;S>I{-FKW1P zT1399`k&yD?%zhjqqv594?Mz;qC3#Z%AEXBlbI2*=Mtf1%4x-1Isp{BNGa@cl>1e5 zK^muXPf4@sL%JFP*I|y<$VA}txI~=tN66(SS(IH8iCU@QUZBkX#{c^EPC%hGU=$H2>>vf;tXNmn@^;ycUey1 zH|L%@5ZmfmE1#hg~0z`LrcTaauhVmfj7QW;>2w1(5Qd^f&_|gjOw`K?VA=>_3;BTju1JOoi_fV#uPIMI>DEuc)}kq{C)Sr4 z5gk|^9Cw5#{_%v%W`oL3im{)^wRO)}dzz!ePBY>dh-%DajQ3+n+&jE5_N?=O1ePKa zXZml@A z$Z`u&vUcZ@A&qZkXD7+5u9mjSSZtVd5+4L(LLXjUd#;k98B39twkUwuNP7=#7Lwyd z94Jc&`?M+(Ezq=9?4Bej`au^z*lUqCR%bD;2(j77rm~+HVlC1RoNdnXoSWz{HayNz z+Tv_@(Y*U-j^0+1OwP4H-K;zXE{9sBMVXrK2j~ON^P22FP)(R^n(OPTs+t<>TIyS> zYO8AbEF=Hj=MWhxC1`7CxR%aV7faDTbd>q#Pkv&)iFQjrVX0Ar$|#l!BmNo8Nu~Lg zX%7C9RWj8y&9~r>&xY@?09bV78)X&r*C335L@O?^Xb$=Oi5~z(Fc0%bAHOfIQ>hat zx~;?(lrVAka-6y_-u+qXy5Ydr70;pXa(vcq%V#BME#DN$DZB5V&uZNt4UxjquzV~{m-mB>39pRf&Gd8|w$R1pej%Bq>P%^ui6loKEGCn;CL zdDlLKbj_EaUYc+389RCu$zaliu~T}cbmT^j89l~hH!FsfhaB)ML21M6>2iMxQkanm zF|g5d?8*=OZ-wlQq9i_z+N^5WT%dKs3L_I!Uqcu+Y?(s!o*Y*Wc;*6OAf0APx+#se z%_yFwE~@~yK`CigZQK|dQU)ghPE^2wc_g8bPS&u(;qVJOVgn5_wCG#233S6zo!=RNg@p|&z zpp`nouECkgLLs#vY7`{{&=)*b)|rh6M4>(%P(y&J%r>wrN@;P=k$phxew`hC@BQY3 zZywwE&>0ZK!RiuXBls7bS+e$b=Gfg#|d^Crj#Q+0~&1I`Dni6=1@aOv6b3$fLwzk?tQwn*L^-yY(1K`Pv@4R!rdXX& zgjBX~pzATA4PU(J@*TIYzZeQ}_sxq*Az0`5?kj~>JY)WJ$&W7hS3_Z~ap+%`-g?WU zg+-AUXbk~fDh4<<#MN3AxFrd=C9Sy20(|LtxW5Nri;DGx+8eQ+Kqvq!u`!BAjLk)# zK_NwZdp6bCZvmH1=>*WVqsdf~+*X0?30tArpUh2&IIxsYq-(% z{M&4lEB46k^DdlaF5;^0TVo3aU|fS=GXNR~5%Y@82KRbFB40TKT|^mS&X@xCE7r9vTcrg{5T0IGK(3m!C}j2+^5BWxTXVqo5S# z%aOBdG2Ffg>g+J%F?h4nMAwzCup)nOLMvo*_b=hOD`~{%*XGOGYY__oIs%Rv;Dxj& zi*kFz(9WZR_{*^#?fNalV1yTd`Yl?xcm_WG=Hd@6&Bxn2)17TK5pe|^MK(huCl_62 zQRp?rt-KtFm3`wz)?uD`*H@nMRbTG=8ryjDQ45_?uY;#x+xfe>{BrZt-#^4tg;}`x znMJ6+lqxZkLa(BzZw7u@4PjLQ-U~B_wTp$Uu0gqxWx+`d1Y$YDTWT&!A9=(q}XttRjHNVX|*}3MmWX8?UnLqle`HQ)OyVPG4 z#_D}+5<8e;{)ElQV9SY)dgaLc;a*C}Ux+M2cJ`T+JY*3Svxv%Mpwac2`Z}~-b%;w8 zBhE7E;RJrpGV5VvTBe=qiZ?nk3!&9dAl*$wmk|eNMHh^iR;{QoQv!1)jMRVsRA3w0 za^1w`+M;|SpQwdTMY6TQXT~6Tg0~^EC*5jCb~kx5On}i?o;Aq88n!PtBCUB{T|-p@!g z5Kge#EtfGkYv4vcS8d8Q+XcbmJU!lSS#+L5y3@JHodKXgSyR<(KMx>co7W*;K(h%x zWo?7?|ZAb!D+#P4o%iN`u4+EGhIcDoh(=HmQ`LT!2N2)dEt7~tPbgq8|6 zkS-KK3(ahn7|FJ0+jDJ!xhOBDfbv{W2~|-QWHw7|4FR2m&56qvM=a%g5^$63y|G?7 zJf9bw*$ly=!*E7U_sQ_y0)t~R4-b8xMEjEbM(z1WdQL{|01@MXvg*4e_g48VfwBN2p?HI&Ws zjXln~1YliX-9X2HmQCZ7#^9rO8Er?rw%6L_uQ&I9k;NY|7i!)8lg5}wjk}0Nu9$cb zJoS72c2Rt^`OLxQws)+Z|Lglst<`rH#>N!=k+q-OyIEZoE8H>ho%;!H$?{450-q!U zesKGb5DiOJB!P;KO$#k9aOaBbGN5efBPGIoE=<<=^tRDw%o?)#>p}|0d%L}!K~U2 zS@Rax6zm495ST!e7O33OsJaZHjMdb)P&#rjWicL7aE*er$G|hl<$^a1{{~;4g6ow6 z##!k)NgHh_yLyr0y9rFuQabVXUEu`$rrzR7xs;ZzVZjNV7|cE^KAJ`yV#;9 zG~>aGFTeEkv(FtpuKVUUzh?f~e2|^U=H0sWnvH8Wc2AqdDqeYn)ouN}`Inub3&eAN z;8%`Cr6=+#=u9nhB>RHKPG5qYqA z{wOH!hZoV0giJb&Q7_wmL`Fn*CS@1Nf`H1wwGI~N-$v=_Fs}o+ve=vGW)+K`5%LAV(&uwAWpaPtdzg#< z=qOfKtLM7^h(+x>#Yd*o4`ImkArcbNt%C)7>%icwct3JH$hHX#G%+ET6 zK<}M(y@~7ir?!vdY2~5;M<29Rr=)JD*NY=q2oN1Y>;sObxutL7pACOGPx@V%&rKx}4JtvW#ue9_$)INH7UqL|* z&vEvBv5(q;Fu(6nI{uc<_U`+f9xh@}xBzoDRl&VI9Vrm_@UBYvXyND&LWK6N*Oc$| zzrw3Kk@`+B=)r9g)ty*@O5enuspF

N3*JL@M+=5TOmeZMpWP%S}tW?mn%%!UMK{ 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 IF%l&Q@ps3!;nFMYOaI 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≀Nbj-$ExlKLC?kWy?B( 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�Fr)E3m?!X&m)tGhMoy~zn( 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 z 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

H+puBYg3B*w{r+F{f^TJK!;~E9 zzk3B~R!%-t9%E=WiyJ3_3ntP;WTpw5$Pyi_ zcE#W6(qhqVfh+K5K`U#OI;Oys*LaewR%;smQzom|0d01;$zs~WOiVu3J={$CcdKhb zdoj_Du~WU|1Oc@@1fdDCB;i`MWv*5f^~u|gW&W+ zb&^KFcuWH5kzGTru{7BZWtUE3fa6k{4>(e!s>l_Q^5IaMv0;lKd)osEP_%!~H4B2y z;~#sPFw-+bK>3swNWM@S2UK{Xj~YRbD5C};Q&U1CCg73bM;v{uzT;BWuw(SrA31{M z>_HH;8DonR?p)kx1Q*z$41kk&$PgifD}~^=Mp}n~PNyo=B9UaqbX@09uM=w`Ul@26 zS`&$4?a=EKiGXCnU$6oj^y1AuB2)CU)?bV1uf}zW%bsz=h!-ukj=%BqXFJq+}&0yX-n( z`%Yqht{?*t%!1BT=nGOLSNb&Q!uu$@3NpOITd8T|o!tJx3(KCok1c0$_-}#e={jo? zJKlR>({HZ9yL!)c8csoe=LvgNw8`m$!KgDDbf6197FkVvoTwvzhYrhcG|K4oCd=4Y zz@ym!z(Ofk!sus3yGgm+Xw+{2L=9xUR_jpj#=7C=H6D~`;PrXAInFF6>{^S{;!IA^ zO(dY8RI`S<6_*wPTbfXFC89nKFTX5Cs}!__(1wptqMzN&lMNarzy0*_S!JnTL9IAL9T0LaL^w{eO7#6Px~U-wFP~V_obiHp#md zDUUTQlRft5$M^>)?)$^0NBLWyf((m%J-Sx#VKPtypaG?&isFUXBy{G2ef--5B*EHm zkO7qe=Bvmlr=%o(`Jl$>mJ=yl`sib?>zY|cO*B(RW#Qfg;*3Q$qqbJ-lWsJ!OB zd2udHN3Ty~kH50}GhMN?cxh|Vg3bf%I#wXH_X?O~8-JOXe^u99d*;U6>W_A`;FVaU z1Lr#BO03}&0XA(a@DY*Ytd#*I6k0@hRMZtD}&e;Pt10lH?_RXkGy&0&`*Bz zA}c@kCM$iKcYfG%{KWES_jPD05A%ITfA!u2$2q_GChwaq-r4iMI@svF^UmHw_deUa zf6Zf?XfC4A;M=d;in&lU=#A&6K^p-M(&ENs?4c6ORRX#fNKOU5w+dhiQQiSgRLXn7 zTK)q4+$G>N9d#AUQ`86-Zk{ph&6=86cat(P%v*7zUeM`vR}XW)B0A!eQ!OegfPq-q z#f7{<_cf!ZEY(;qV{(q%GqDs!UO3BYsRx~&SUzR2Hy`e`=h#SmMEI@A+Gd+$7E zT6+5j@9M9(W%G^^RsNqnxGesf8*lqX-RuPmro+g^?1To$t6W81UFtEx!O7x`8sI7u zttHT&(7;;BTdmU6}e?4A%VVNTf+am#sl?)N;#9Q$y%tJ*>K_-2m;Z8V^PlS zs}cA3c4|Fdr?|%VtFEp#$tu}ScKP%+A5zu-|9vLy_B}3lKo(>P+dOg5aW$z)N>75t zjRhc+YjA4(vPDsHNrwKq| zm37zvgSs%t@^(dsA39%^HU$Di(jBCV-!b^sz`W^PQ1-GyrM$>D45il^Pn&G+A9xSA zCfSne)Pr%d{l{hr5a>&ZZ(zGXz~jVBv6`Xj<7Ab4kO;k=C3!lv)gvZXK(&I>^@oA5H zUA_Dm>4oS=uNV4{K@%i89>W=D(sm2k!aPrT7BI+^c14mbiE@pi@q>zr%1R0O+m=u% zC{YiIk@%i1WLw->7PpIt!jco2qD;(TDlosl<60t{z(|LJWrqzWo3eh+`;VODFFp4> z^SpC{S?*8lHNX4on)knV%Llt3f6u{piM_s#f*mV=#qy4!i%jmp+RnZAK5*mfyE@Qh zXiY{}dtltUe7P9eF0Xh@lbEcVoGmhaiU~r4b`)u~1(=^kFYG{c8%^efop0;dK(rcaEA^+Z#uowj8l2xaIb+u9Pe_vUCE#v znn){W7$Z>ka4Iktu3^>!ET5D@WK(G-Y4ecBpUSzIhz;Yb)DgI8E zNPgvKjfXTfQ(}S<(3W_t1P2K~DoI5vBJT+Lzm&lIr=}UKr&rrM5JQ1dpAg<(#Q(^T z-qQKV?T^0k3YH!G+HkH@T7+>X36p4>3-GGoTP03P+S|C|Dl9J09q=p4v9g^Sz~?bl z@hU~@GqhGJNoLKvs$J#j_QFktpK$nBPu=!Yquds6VwxT~7w>?bAuZFk!9=-8^_8T9 zAp|YFJ_G`f025PW1F8-)JgN92K84mwk*DAXuq-fwI3bQAN|-rpoD^u-IIrOXD3O-6 zwfR=Iwrbnj`@7oPHR;6DlCTIimTUj;VdphhpWP2aR_OMy zHeN_yv{n~EwlqNJ%oV11Cg)nsa59PTVF48p=A6JtqRgTlsIW|twA4ApnWNF8rK#I8 z!%e)dD%Es+C+tbr$P5-YC4cfTr(!Ro4 zgKlSaUP^jl@q@2#j5Ew;drg|ME9P8%wfu?xf8Skp8J{W{to{2IZ%^5Lv*wh(x@CL$ z{fpUOX-q<`uSedFG24X8p98NeN-{Z0GI6%5y((ds(8%3Ous77Yco*%5jKBr>GbOqP z^U2+Gge2hM!1&LyOiGqvv1G&C&2fPh16_|5?2P{ixNK%e=O~uq~gl{pZ;CP9Ea#z24db!}K%0^#$Jb2=?(|-+t{PqX%+v zCV8*QrbE|24x>h_OivTph5n;ofUe-rZ>C*MkV{||sG>BpT(;C~aXDOb%I$hxW}bzb zXHv6YQlv#q9#d`NG+hCj%gvb06vZ0q6YXS}Fbm1Uar`7 z`I0TURjYq>{kadynp*$z!Lef>zx3OqIMSE(oNnQJck_LE;RpXy|M=JQW}bQJMSk|4 zRSOnvoM&#zx#j8Vr+@R-uU~kj{n*|IpOI`mJtn@JRYMU__=tCx(0rX35)BM9+gXQA}B`O^{_NN7D2G0EpogOpd-qGN+6O& zWF)7+#WhfX^#WEzvv-IDYsW!5P=qP|Wo?5p-el!Ghx1Ko+>8}~{GzeYp^kwP)=5H~ zoUBV$U#|^UuGMz7&4m4PM+1S*8_ctFkN^FCifY zpV@j`p~!OlO=Ne3{|#y%K2+vS5)_68I99XWYh%_^r?hSTn1Mq31d_#ZVD0T5BYNy2 zn*gyHzsQ;ZLsBQox*Clh+7yXrolYQ~#s5~v;Ljn`=nT>60!Q*1k5v%Hr>CZvO+Wx6 zX=+I}P&X7bI(CxB0xL1(G|?^V=|5oY?a{2aYEKVY6G2{d_6S5(lUzA)X}~h4Fyo(r z>~Vt2<1nBT8XC+g_$}L(C___p9(5FCeT9!g`?D@zjrK-uz1Q%g252D14)|Y*`=+vQ+#VXUny(y<@g6r-)G-@dLQg!@^Q52cSClk2rl6^m1>DroH>~j0D_9X z>7ccT{vd_^;3qCbh88|1aH$9njz*)-kMs$}Ot4eDM^S(jY7D((jR(Rg%bqa~?@n=N zE3qJW53zXzu?&;h1v#3Q&HOm{2;$|;f31D`AhhZ z_(K>pcp3Vfz7>1ZA-Fv*8}vA^GPXR#JgF(#4W!HDYL~iPid#^pn~-OLY*Y2I0l#1Z zSvhXfoP^%q-`#lc3%$R-?Y7r@ALjk_cmHw0;;wspc&ljFcJ$ewJap*E@SiRG)(sms zZeTWLZO+B(j_7+qpF9EC0$CHS*Z^#DTD*bIA^7v5#et_0^n@2*LOPi043UJ3Q{l%< z%>x9torpVL))CZssz6SBL6_nz$x*#Mr6rb>xr}W295?<7-HB|mv=YsMF1GQFH~1r6 zU0wWFB>tPnPuu?dXB&%WzeiK_e0-0${iV0% zvy8XcrC)u;E`2NGlTR|<;)lNaiXVC_<1?bQB%B7Vy~cFZ6U>K*c-~`~$&7Iea0EFa z&!x)QEP*Xn-958~1f$lNpsfM7V#2Hj&YU=7eB8DqW)O@C2IDqG)7+4VmJV8Vsbuu1 zk>y+&+jC~jyXihWLDUM=BLYT_|K2s8iG?t1F1pY&x3r{a+SK`l^D){2S9WrW(~_NR z!ZFWMaIOa!z!CTb2YShL=p__|Q@J2V(OPD}$R{-n3gZOkR3cfKB2zrBMJZO;!GN^OiS1_0aPBfBGa( zshU#Mb-^ax$*x!4@b#anr+w}-#V-`cTr=Z+t;loOJv zjiubf>W{2gao*ze!n(cFJ=1*+l`}UL)$W;b?HXDe<&4s|8jfQhOcJhp&WyvBG-?Yp zI+tJsLWpsPpvR$Q)a$lr)%`*Gg0z9$Zi)*&TfIzxV=~Hh5CD^M-8tlBr!9rB9}K(j z{S>y1#)UqB!zs;?;?!)SP4(7k+EhRM_=1c6N}EdAYkAudkpaBImPSunGG)RUg4fn!Igr~uO z4RJ3%fxFIy(0Zz!+;tWQBmneK%mGIUL#SBMH&2Vz$hgVde=qwkBD_E65AbegWi|JH ze@`8+)3$N`+Pxpo@I5Gcd@YjgC#zPGfE+l^tDXZO1kQSOoKWNugrJ~54MKH!td~hO zQPh0=5xP?F8ghsPK3(HUOH5=!VrpVaa+2C_0HNcAKp2n4j3`J7T)WBs!AskHyD`{n z**ccXci;O{{(gv785>W53>|r z&?1^x+K0XuTEvwP|C+z8(!UwLk}~KS<1h=Li{Pk{62KK0KS&PqPYFhn8w831`Amp; z;4ou?Ju_px*%W6$6Qgl#oDSh#EmWjrxW&kxkacdF7Q)k*mWEL}1STnc^c8>T4eeU~ zJO1i*fB$a9<*fJ-;Ico|@jm|^E9yVWQoF#AxwcanaQ0yrThD5XO4?>!^c5O0zhBS) z#xL?o+w*+2)KBCoFu~)y$yT@jR|wX zL9FzihY!JD0Sg0h`m`L>lb6kyRzAHve{yzVPGOeamSHs~B^vd@cs4#k2d?q2kOsaU znq-dXBL9MWu86rQe$p9c7G}#h!hV;wvtsthyC3mt?mf*PZPBh}xAITF-upj`D{S}w zSxV?Xy7iN1e%pJOw{1vngpK#`>HM>uFp4X``IJ4v#veY;uKH-9qs=_8sJ}SBf%nuO zVlH{}F!>UQjr%&`#iKY`rYD1r=nKg;9Ip8B$eT@U?1-ShD7?uJ|^jP>boQUT#7mPLKXQxR?;I^srSd4&YfSc>$0s+!TBV6k~FGOb9(+aS%9= z9pVQVa8XLoc#7A;)xL4?bs}W9b?8uZVMD=i)S(OHb0Pu$6^i4V5^+O(vI`+svdN5orvLv!Ckc(o2Z*C$Ys5?AK`RMXF36VUPQ-sLE*+JxTV0en{ z;8xNQl;kUgZ*oC5jV6ys7IuxYx3IkGr>cW7i<}P_`P72E$@XcEX@osVL2gE(&hC$F zVrNMv+D;mZbRzWWqMhU}O`Xf;(HUQaFHv{oOTHCx%|5>M%P-k}CgZ>TU-mrB-+wU> z|9p{6d0Nt!UuiDCw028gzOR)Dtii+@&=79t4@QGF+4rW!(3Tp%?Z#?Z-(RL_J!|Ln zm>5%hn#2YMVS;D8L57rpbOy(&{w50OTTBvhg*s^P#3E)B%hJU%X7kyg_joI$=Ke}= z6Iy@J*|!Lspgu2w{3i%l3wSE5baV!R=!KJROZd}`Mq`38!Gf2N+b#4GE%w8J7pbw4 z(95d*@sWD?Fjr#TCkmSsi}KkN#xBZu4&R~J_^rn@tRsWQ7(i{`K4{2AMWEdMiYb>Hs4{_&50m74ClR?@Z`lFt6sTlpD3`5;IK+U-X! z*lPWK0^v2H8V9Nhf<53%rnwUazv9D&3(TKJL!9j2UC<`eEXcPIlAw!0oQ%&ikOoHP zJT{mW>QYKj8X9!*esAifq59-1-=WHbzW*5qQ+hymd9@)wW_ z1b1aX2Tf2EcIck|cC*UAnQ({tZ`C=6&QxYIIp+)_Whx~zwibuo!>riX#Cv?rP$1L9 zFT9!D%=#rtDiwuV&=&zf!4BHu49Q4Gq77^VhQ3ztm?EhW-X02)+Ls{PMi3mr(v&PS zde5HcG*Pkzhz2zUq9qNfQ8@5Gz~thbJf_3T-8k9d7sW!<^G>#z&7JH!$tq;+4&Q$z zA++#q-tmXmewM{MEzcc&Rs2&DyVv_6^Ri_uU8*x0y_Gr}w+G*{VZXs|kY>dWXO$vaD$%YNG~0x@IJPd{h*QwM#Gunk5~X6IN3F!c;uGT&akUyqG9XgpkPt#k zc1lxE;J)*o>#Lq6VZ!I=u} z8yQV7hK+DSYDp|hTjLB074_5&R)lC4U~b`J3pTy_zj(50K|>HIFbIo6F$ZIH;w1KK z41u=?M)W?P)T=2I$Mb(;KX^1Py+UAM;%Z+5{Y}bM#?|I)kjo)s39-lLiE|@dF?X71 zgh8NM>hlPxM=NV;kTFC~q8dRD->hB<>=Jku^itghxQ&%4mkN1=R3)R)umP^hJOf2` z>2UT8{}@>IIKW5r9H5jBJCd+R7?H7OxO%t|36u*xfeACqN@o;K9g5!-50x3>KgV69 z1j}LDhJq^am+G8zl|*SJ6qAx_rdUk&pzJ1T_iOyE=k(sV{%2l2*Z;+V1J8f{=Am!@ z?j~)I{5*3%{W3HC__>Gw@X>SdrF;;U;4~79F4utcCP`?+xma%WV9jQsFAh+aXw{G( zH;6uQ%#dhIkn}o(8Wtah%<(uZbA_elvkBtte9!C)$}gBvJaty#tf3@I8a;_38W>RG;M?PZ?!j+U4H+`ektw#y;2c1nA^x&5c`oqpS? zLBD4DO%xt~=bguw-gFaupXz)(7>)yZlBg{8&nSV#nc|a>j2UMP&1cKt`8cx(NPLnz zYl?Hqgp5=;!v@SNIpVy+n_Gq-*D+^!VDMr_o+cB9EFohaIDFgm>!A!SP_yX`Fb_#k zWkJGFY)w)3tCRqn5^kjAA%wgb9I}derXJoV!IbTy!yAW=qTL~!%IFMDW}aQIUAOAG zj0#w*H*S$n);6xT#UGc=H|;n}J(E1=x~09kDx|+6MrFjivFD&>LoZ!RmO7a8Z=FHPQ`-)=s^rXV{OV8i*%J+0teTz12M7Zmu!E$Nio%Aq4 zS}mQ@oWv=yya`##3|SB~yK!ilDTsIn~Z6@Ojw06fVwycFnZLG<6OHKr33ML z8J9`PS{V@o3-(5!-U1uKySoJ*w@#kFeDU&&FJ8WMsocP}^L?yg-kP=L_`;J!ZyDaT z-_WkStH6^-@4{xSAw|Cy(F&SsaEgQ8M;lQJf1d{-g-TxAN$*>+bn$Y$Z$IBE)+z7% z*0&TUspMsvmCyzyVdB&BrV0wJVP&zz>?V9pQ<0ED^Rz5fU^-yB)65D7kKfT|VufE?Dd_VMef3Dg@5A1g&s1eTx z+|Kxe8cz-;rYT8j{)9A|su#M^Y3L_9!9j`rOu|}9$YOzR1nEL4J*q=P3wx1~l*5;N zy0^aZx4isWn^&5*)z7?P2kTEI*laVq^ z2dmQo<<{?C?j5cx!O-Rd7(a58{5jT?sW&LSd1yAZW~rLUEK}7?tVHG!CN7UCSD9$N z8#LM-pfz?aHcc=KL?KNWtpm;@)msyA)qvD^4y2EK49`gray-s>Jxp-yK8l(Pu<(g> zVVdE>1c8ijS~vq~kF-6U^n+E2CKG(DF5fP(mbd$QOuioIvip47A(szJE4^R}slwZ= zfy}}Gl(p3!M~)QbLqdN>HWUPm1}dQ>AuEb_Ze&T3+2CM>{}2kGZRBS||87uus1xsr zholFBdz?{+w_s+98%m=kjSp#9pcr$nnW3 z!T3AmITSkh(bwPyQ^N2}yWEv!#(yRq5+~@fT7O<-@CT`IN(eruuc>z9f-6?luDW8u z#@dG3OLn>4yDquy*L6#;ymBdh?L9KHsA%R93 zvyiRRv8PK|idL7VMTSti)aBcE_0{5bshHI@v;93i<1)IH@$@Ik9sC<3`8(uiWx9`* zWS6?Lac_U3ShZ=BZ?DuPcLZMBJmg)dwSilt;_zFBn>QKP(39}}4#lzJpLen~ zFmEQgbrLoMlWJ(V@Xur9l1to2AzPEb0vgC(OV^tFg2~nQY2ZNk`qn^cYy@ODv*#amP+w0{If@&U^%ax@LkAO zwLklM`<3ES{uNj=Y3|ma@3^`7+nbuIuRl<=z+@NSW?R4bE8Fc*8VL8Q>3a1X7s6mEtJhF+V0sWK8I@EOE z0_GmY%vWpJX>(>pWl}*>zI*kQP2XLRI$~n%Jifl_CyNuLljg*AKUoIxAsmL>UIf|@ z(qyxmk7P`n!kzqzzn3e+p!RrPB$gW+;G7koUiVjv?|l zs{w4=fY0GUR4>AIQhd@*q=(2_QLX{WgHIWB6?n~bLwQ-d(Z#)4T54=G(mO zj(TzfNHq$|^Scts;=_ytV4qI8$py~m70OG6*D&~9U%|)d-k}={9M)+Ea zi>5p56Kxse2?^x!n)eSve^{esA+Q+}- zi-!J9sv7z??Xtsb8@fI}xb`q^JNQ*k!`cJh{O~`0*^NKd4EYPmb6{~o-(3y>YW~8@m710bnWgwF zU>KLqD9v#z79#Vfe^bzvEjqm5mFk~;wEfyY|E&6@1&3XiOqsK;dV@}$w65al(@(y3 z!8*52pPDg$-IeDTE)(z0pFVlUjL8L>{fC!-|F6Gm{`~vP4;OkCy?7`-e$h*BTz2oT ze|_&|t<4uCBu$$3-F*u@zMEG6Xj)czc~*Wc&d{?pVzrPjtpxY^&rs5o1-6RhJZa2K zfT@X3L$;X(N&fj~S4u0}+XdEdg<4xHmE(#WoKARUPeA261vr6XA4se3sHocmKtvSg{L zky#$$k0Ir&nIC?b%{hJZo7I=K9$fvw4{}#}w{L#>+QY|>H~-f6^|qhSxpCd4JO0nF zwDEiS=kLHI-p#&ZpKn6k;cqPMTTkp-{p%ZS#ZUir^R4Yy9=Y>f-p{n}zVW_z^(6bo zDdk(g2l`{4C*>xM4Y`O3@T~Z2%aO38)!czZNkMxD=8yQyW)cj9{o`?jeTF|uaKJ#J zlh#l~>MFW)1u8)BzvRSZ1RzMYL*B{&UtBhhYN@>A)1yZ}?O45i+vU7_Uq^@T- z)%5MnJ9j8LYJV9Q_|9`xpheX85g&x!r<|iHPQ^#WceY=)dHZTuSuO2cSpJ=xm%^x0 z`LZ3r6IR1kJljeA!jq6+KOVFm2PI0XOl0y!n46#Gu(VLD2PpuW>mUsVO*bnW|6vG2 zX>L&+aoCb_O2|DYm9@9~eZi|%SKq$*qRW<*Pnoyt_RZy2uAMbyzV760PdsrOys7Mc z*XAEjpT3~Tw($}CfXplNRq>8o~Lpp zd~0hruRFjGFl)e{t2=r4a5_JUM7-nYUrH-LOQ4Lm8RKnL#)~{A(i&K0^kE=CtByez za}4zjG=`;1m#$cWZ~BVPPPHIipt!NI4!LqO-=ojP7?2a}CmGqna7KV9{EL8G zg7EozO@TS;b6Z=}`C+yU#y{&X_OU0eW-Qb~n z^v5-ujEnvwaSav{oF$>S2CEn*u%SObB(TARt@2v^9{oy0HHx1@?yw|WsCo^k42x~R zGF6eE z(M@n;#fU~B!Ff}+ZD0oN(8Fk~3rWT5qxOEh-C}#WH5jE`S>=bt$V=mq$H&f8^lM#YuZiFCv~GV?_;&25Ml#Yu&5afnJoOVYmm_>Z;oH6)P6an0(Rpn)PMhx}DD973};fSVQEJr?}B-j-#GzEVQ_!{sryBH;Nen-E>Avuq* z+9n$cWDvFyg(LW;-m4R)D|eHh07V(nmawM;V(U~Mf)5c}_Xnimc(yQlW|OUn@1h5@ zLf)o4oO$E5mA-o9Oc;br<;fO!FN6z;Zlrq{1cQjoS4Hhb?3G}0+E_V6(r;iqnsVdR(llgIr*3T zpO1F(Q+t2RazA^9>3$+}ui@zje|D(i{6l=#V>jOZ*`BxAC9UFt*VrZR-v7@BZ+qpD zzu)RxTl4pa75WO5z6NnGG>Rml!efL~hB^huEQB(hq9)q;sAUrwd{!)v%ETZult1Ar zGFIsW5zegVvYkqrem2z%lIyNi_0+Kg-5;HpywGhmuQxBh9ssK+_&56a4ntgRwfg== zzGGqokOt5T-D5|tDQr-}Ouq<}9iYf&=aA{(z++YKZ{P*+3F>Pey6fe;H}9T|jHMZ? z7F@S}@!RY7?sKO!8Zze3Ty(Sgp2K`4(g;YJU_TC#FJwFfApu2`iU7!jL3n>iPluXK z`93pL`oxows^mo|5Fcg|3(f>x)e016IC?H1VWHaeSZ6W|sy+t?N> zGp8`ic(Y|o4s$xQ)(Kfz^-F~D<1^L?85uv%0Bi091Q&CrW;wU~2VX9X&&U|R%KzT+ z8HnR7t*G#L%F8PjRxG?=foHyF{`u#X&nurdch2ltl*(Iz029S`5ObPLCBTSoWFg>` z3H;gd1VD=zm6E1OshTFYJTVVWBnW}PR|<+KN*vkvPCr9`4}2c@ndYz{JJRBI=$QXY z6gbwNzI(Bm&I$eao&FX0E62aiD1T9}I-^`=RK9-`_PciztFrOcHubxWHQ4yx(06(= zeW4f#7NGxslK+o={+~ww&u{AOV!d74VYiC{zH01t@Rlg7_j$GZ6dQB8aOrah6+j56 z;MEhLFOew;zLP`}tB4gb265pBUKk(zoHm}YDR>!S>KF?Pa$Nc7s6cKk5U)T4Xixxf zLIe8_iCj8ZFR7X|NsUk{s1v{wB7`9$}MCoM3s6J;48VEG0gv znz4;5m;dJO-M_hH|N6p3dylO4c~7QZxz$zE&l2ivoowx~WBfp;?EwGcx9JkQ{x3T; zD{YVV?cLk=sO|B8%ro8fCrm)<>yTT{l|!dgdq>PvWIzG=z$kN2K%F^k7~nfnFqa~X zq%Dq5x0-{Aiv*aZ$Ovd_X<`1S0lujEMa;B!AKX9W0$Sni3^;>yds!9VTYmm!D82pG zx8~1V6L3mN-ZFjtxz@OP?DI72b7T~e@_h$tpqZAkQ%Er52NSLWdJCD2E7H@+9h07s z4wepg${6S*3v*67!hWX&Bk!rfCLk|jE~H!S*Vhkr(6Bbr7>RE1R~_dK#_j&`ZCu=# z4v8}!bVfNK;FCV3B$0K0U>@+B8vEY?z5DshiN z6L4knlJgYySv}hW_JL0k%Y*f?$}PO!aMC|FAor?XO&YG^2$a|zmq zZgFE0XrDy^JwaOqiH67ttHD8pl*KjpA+W26`x!{@@JIb3UxV;odOGb0;61pqbTYzw z5q2mgl=3K*@!f zIUwbE09iM&_-?*WTr><#+xS+t-&Z@7p8fF#fj7kqetN>Ifh`H*A-)$bHG+FC;)aVN zH7I!A-!gc^UGH6O7~&<8gw|G-ALDiDyb1U#syzGG#}!2oL>!kceXtM+jEf1azGK>a zndo)J;>HB&s91x5-26an{%8lj|^AwCkR zN@CC_!^I5A%p{ny4zaMYO?0)iD0^E+>P^Bj_;*RurW2U7>{8+Qb1*d(&4{uE-6;tc zL$a2fyNU28$W=??bW)NCKjO9})_Aktqyb|QB8ln|9(?(AkuLXS)8qUPhhqya1Q7(8eqb+G5(De6zb1)=vtwdx`lVu zT$&MK^uxgp&5blRX)-YSRo}xl? z_fCghh4?pT1t9(v`@FQs?_+RN^crze#IK&e`&CD`^$~vV(C>MB zQyOpU?q+$WXIb9S$C>7!wL9atbuWKoMM6{C>HBWo{m1VgWO=Xqs2E^n8=LcN^{Ktv ze!>6wDTadG4zpj^1>TNR&aJ&3G(CXU2F8Z!qh!ItD3oQ2dRY+sK#k z)A(E`lqy<(K5{Oh?GNWng-H)E4-Gu3xL!&oS*z&$kR1$P{WhuCdmLYlO-*cXQxn-! zyUulLDQ1_7tT5C>Lwe&>A*VEC6ZQXr<^}IFyj3KnDBFM%Khx5fkd~Q-nt-GP=QyUsg-c^F{$raW&zzfDmcRGWuuqs19vqxbIMjR!y10ygxGTGXt+WNre6fB3@IUI)h zA`jDQv35MyoXGw2{2G!|3qaGZMg$tD2`1`b0rD5aV)UAJ=kecJGZABT!#jyzPGaWN zC8_3^%N^yF%>SFzrQsMYv@f*u!eaDh=4zolrAmT-n0~Bb3Mb(bu{S$2V1ca?i4>LoNu9TjqHC&4Qv$$2YL!i|@E&@zr0*J4_OTJoOhI$xAL5$a@Afo`bhf`HhQl* zz;bT=uk{`4-&u#5IX=)U=LqzFu$1N;OD(1hU!tQYF-DfeT6x|`W`JT>LVf&EvL)%b|9UpQK&R;RtsIZXVs2X0Zi54Ab-c9N89)(VR2+vng!MxM?!b{D0=~EvuAB{c zzo;sLq#c0$mSb|xrv}MtPYsg91W|+JA^~}Jkbe^CP&o1F-1jL#lGNg|o<8^n(-(jB zdV`EIy}Bc6*8vv$%!~ZLJ~+fbXsv5_>-Dv7+}0)+Te@1*VvFgw{9n(#z`v`j_!sSa z(I-Y86D#VX7eVvmN#p5c#>Dvu+Q( z>?g*+FT@9WhVcNcq9G4+oGfKY^PF>l!;l=<{km9KMfyMle2^r9644B~O*#M;WXkgt z5g?3!yHVi`v=D0v^8h*{Y|P^WJA49grUV^4-d={g)(;xutDWD`ve$nV*q&VrNPD zuN#-$)@;D67F$9dJyrW`-F@}`tC57V`O zGFEK@l0%5)+WmtLKe#!{>^NLNGGu_Ft%vNy*|=QmKS5O}%^tZqG0x<3-KEosRWF7L zcOv+L0w&{7&OoGxp|DmGL>T4sVEKCrnMFjJDVR(90C`R@x3D)b+4{Vy)&c(I_A`ww zNJHm6kL}pAdDnjA_e4L)`}aT2Hy!Bdsb-;H{pz8|S#{w}sZ(2DzOC)fdp@aH{F|-w z7GLX_+VOJbd$%mQ^;xjEx8Hng$~yD|@lvQJDaH<1T)|R_mgNP=kxmbzLa9JTG=_{Q z0KgP<9at`nD+Q^5V048;*nJx=K)*5SL9y<>UuMF|f*Pje0x<=fU`?6_nHBvh@&v=0 zjBC%o`jifJW{0kT{#N2$C**RqN{QDsySiaIRx4Cavx*&6O2FS5Bi-N(MPNVzpQlbD zRn`K1g_6{W%M}`>8!!a}5x{0Z__V^257JDGo2ZIczmhl8y-S&Gu1J2}bH zV>UPM{B|pL7qH;QDY|%Z9-nVM6nPGH@vbE%x4)K23ous@VwfxNJZMfYvSEp9R}44) z7NCDYZpQ442&YhY%f~S^==UL?LnEf906G=<8Y889iJZ^tZ-_C8_!`EpbS`*Tg}(u{ zAbi~ogSXst_ARh{6g&b4|LWl%hkg`mv5@LviL^vgnAgW=>JK}fQvZFOUv8%u>m2kb zyfeI4gC48_uWt+EF6S6JbTdu$=;P0EgTT2Fs+7Ui7?Bw&#!RtA#^`$GuKxe?6{ZQi_Y>WUwA zajGMcM_zVST#h3u9&sD;7u4&!pvya?3#4bA=Fw&(tdav(;8w_lO%MP(l3F(d^hwnH z5NHrkn0GLqd#pQB`jc>cV_9ieGPjWDa2!zHvVDwVvahBB%Y6a-UT3U7-i$sM=Dxb>4SPjsS)&0o~|SMR7yy>VenE1Qyf z*LBUPnbO0qdvnFzDW^^!KX!VZlE-WVr`bid15#bD2&1BQSS^G?HTKy#YljI}CRi8@ zxar)r!*tc^lpYSeltJn`(`5ilD#4~OPo4C9tVw!4and48iF7%rOHxRH@ zynz*KQC00F8u1B(`CI9W>2RT#-SVp&E*s65^(-MmrukmPtG z8p2sb^*^C?k^N@V$kT*x%K*YcIkW!JrpsnF)m65dxy$qv^LwJ3rR-ey-C=%0KK1x_ z>vJ}I_n3yR;x8P_Imq8VK3e?(>%-HYMCp!~954~ckwxByv0mmglz7yY`k7!jpqYwP zOlJ=HJJC!{>Nrzh*n*L}y8)xhs0ONjfiY7`k`O)d_~2E%=s#gSM`llO1yoS&nK@ey-C zIbeXDW3K{qKy!!RKp&)%GUy~sY4YWTScA-fHp8(=oV1J-9FZ_V%t*vgXrthQd*#W; z*R!zZ?$VvSv%8s}*^Npw=KZ_H<^sI6i6_`U;lF=j{W1Q$22b4k&-Lp!{PS^ITlGqX zZh>D5cv7YbFL@@sByeQIdqx(SjA=qMCAdK-8Yj?*Xss+VrlY0kBwJL1!JWPtN~&iZ zIUZ!HXW(v}@Z8iJdX6=oC~azT6_l+Ry>j>3Wx*y#Z3@ngXqEVX${8k5n2GVOU%Bq zGC#$dq;au{I0F)2At1$~qn22c$eTk=iO? zj*5ZqLS`*9IdlzO0GI@pfZVi0kc?P+#3GG7*3H2;Nv@UL+4;9D{C1dc%5C{qf zt5_)EST>rQl)iz(Bf?#(k{wB}Uh?GYuA=@9DHF+RGO)fch6pQ>N{oQU|p8bRicK2XV_Jz5!VF$b;)i9xTcPHW`)} zE$2i~XOmfzoE3*9JUL74WMOZ<9k<*TenH5JD?510*FOaKbsFS5v41#NXr=YyJyXPc z#+&i9K=c@-HTVwi!Ru(_NB{=mheb>ZpkkK6fVV;}()&oghji5Z!tigycg?~Ut%M)j zu}p3J;cIaIwUncPW~X0(wic(rhj>^aj4?R!&=2gJb8tzJ-J~3LRVZB`lE8ncPbMFh zL9c{xCUP(F3&57>_semKfkTRNJ3@UxNSz*VkFr3Z|CT}=N&o%#Yk6shVQ2YC#P+%T z5XBpX5kWqr7(asvm@hpPaJ@bmYfq9AJOpqt&QbWSxB=@AOqf2`P<8|q-ToT;r#09= zJV5Z8`Xl=cmJAjY{>t!mdH-peAi97Y9;WFZ8&kILRczf2Ztc~9WAYO27m)q3LrN<`RU0x9F2(+ zrBERasa%A#96iKZ#K8k*aDna9uEXtv+$Oq$W?>Uiv{~E(Tt#GLXlP_~BoN%85up(w zA&H3=s*(y@B?Gh~-f*1}-U)Z1)Iw|plunqEbGT^2!qE=DH2akk`OIFH>RT%nP!*-} zl_#tLol0Ou{{qcEcguOgNqb_qW26RWh=|_e6h*iL8mh!d(o+X*Xr-7Vgy(3D(1vpx zzZC1&LYpM)$6#$ikAebqj@w8}_uPS}hylS9-0@vJOpEVE#saDzJ0=#O_K2|HKrPNo z*Z}~-&K>8aK*qUZz5G3;^xW}YxkAg!>7_JAtW&czAJ{0(Uoi>&BGe1iQ)LzMt{BUj zOF+mBI={RZ=r`;-?J|WXgYXF|3?ep-K+JhaC?ULM-FDf*^3)P}H*ZjXeF;N!X=-X*4>a041=5=uivPeg%PVn0-X%MH6(^B|Y(Z(Qjqn z58$_6N-wmJEk|b<7<&W;7dk6DjiQxlSS>L%R#}s@+4Q5|3pJj3C{JwS2KC zKr-|`ryZY#k*?QO47*pnQ+pZ(t7*9Gl07LbEGf*0;j(4B&v&F&V(o*Z*rp7gd9-0s zeIT!H<64}1wrd=3pOza~N~`#h!nvo_icY0K#BQl11-C2D@*iCvxP!T;zh*DG?xisv zoNv?%)+QVc8Jy|WVy3~WjNf`^lT3X#!KN@&&SN$`GPf0Sg!nyyp#ez;NhyQTu!X$9 zD31$WAF*)CQ-Opq0ii_FV<^L?;NK>(Fv#(VCJ?u7XHT7+!qU|mxr}#k)A6Hj*M?do z*Qv2^1y`x%9_JjBAUJ_-pmS*UArl0dI8$j#Dg25C$i?L9nneBsqH)5nAyc;wxkfH| zpq}suKeS4Z`b0Pv!pV1H+k9`#U5O5)F5<+Du zOs&BrLb0Mogz=HNv-N_Pm@gH8E+16FM@2LFUUvnOQj#nH#+HeGf zU4=cKqRyylap?A2_Geth|Gnjt^|g<$Xqofvw&ufkmi?=%{MW~nxWQ`9G?%plCk<3v z%^_84{-#Jr97!jUNM)gVAaKXSI}L$4;uXL>5b@IL4Z?Wf8tZ#Toq6`s@m&Ug`?~c?X#YR;_Z;4Q zaz$b+Q#uU(%dQgpF^`w%8g*sR&r+O;a3{d3$6*Ia;P!%`nKFa70yhL%!)(N=45+av zY-3A?i`gRC@>o2(m%YgM^S5-JZTuttl~UWq@8UP}2439BCVj)kGK6Wd_YLW+5P62@ z&u>Rg0#*nPe6>SPMR;SGwR*69c!pZ2RuAm_1%1?`F?dF)yNhUg3Od>b{8SKF75qu3 zrPJJ_a#}nZBs4O2oyZ`327(;Mvlc;o$*oPJh>jcXMc%1!mcx7a7wJXm@#(qc>kYpK zddp&NH`kCZE!C=F%94RP$hBY0-^<{~Zfpz*X=p^DhcLN{mrS3|YSeO?FR5^#TelNf zx@hb&oP9A+pY@S|36k9lE>?Im;WL81DKmleLYYXig{0+JiV~Kc-G)y_CxXKbTx6*9 zOvHW$yA@%4t!ohIs1}WQxH35ez9jV1sx-k9eR9hKbGKjI)!NuuezFlxz}s@ttJ=ER zA{Gsb2`MUE{`yB2^7XqpzX77Hz4*RYK2WZzV3$?rtX{#%07h+7^$FDCk|di{4ml5f zH5OWu9%`}yFgAtMV~X`BL+=MY0V3TJl~mX3g^yFjbf6`>RcC=`b9nDm;3Jc77~um3 z3UveGlsFbea>0Ze3Jf(+9y%&}B?8;-JZMp@g~4}i@9WX!!s}pSxd(aMdwY4`wM~~C zEP4C%>9<>DAcuQQNTekAMhtJ||3DpL@pUYmSw{H%S8?2Sl;B<*49?#&acDsjqwBR z{!a0H!c+}DA0yy~S4s2E^<>yU;>mq1inkk2wzVBb%_lU5@~ukwVH4q=DQ5|cpisl9 z-LOC?dkGE4KZC59LvtOeNCO$LJ`N1uj3wUWS|XRjqcM*%snqrJ=g(b832R1YM+X}< zU8CH()RX+B^qUNQyv2{k@W0;{UuVjYwzRnN&7N#Id4V_M@@RWIbC9bA&A7;$aQPpd z&znHR70c3w%%{7rlq+VWQTNQJq-?>Md-GKA9Xm?^)2PExqhqb#q%@%>%uW!5n+<*F zijyi-&>}16^}2ZQ>Ye`?p~p;8%)b^XTI@^@&jDu`bQq5u!%0S~f$W`;IaKNMpl4Zv zY#qEG{t7=c_Imm2#6j6JvB9+%4g@nR2mOE6;2dpX~&ujqKz|>Fz_}L3Gl(Jq1ztUV=?X zfl@LdGK5i>Eij~HU%&w=uG1CVBz(n#_c&uAEyc;LJ!9x)x0x6zMFI(Ax00C{>IRq~ z0G@%EhFy$evfbL*bF#JdWKWOzk}oPM{_ixi+G=vT%3l53H?JtwXQb{s?z!g<7kNY* zf#V=-thQ%&IsKv|K}|o{omlvRWrcz+rE`&1f=J8ixdy!no@t-l5$`=Wjc|`MT2jcF z3^d1C4?u_F+;EN<3sp`C4-N4FbOek9=s2f+nCyrVgX1G_pwas|39fSq8Es^zdp@UE>~JwQxtMMVk7eEB z%FclkShr!2&{2|2nu=r)&CVg17F&)6jYYVXwlKN1Ql)vsb~9!)$~=ZyqO4IsUceD= z8k%__s<$K~Hkpl(F#{z4@u?`e9sHdaUSLykR7`#01^!OQ{co-bblnoT=1tzFR9i~k zxbMCa3{fgi~0w7wc1N?|5mdv_r$s zj`uV?&}3F9y@?=Z$wK$gx&g`)bOZD!)QuP?jB}&76>P3>&IwD?j$CK6E(;*x~ffnFN|_R-$P zg+Dym$-8POF2D7&c`R6+IqkaCyKy8FY^OvhcPKivJ-+h1mh`_M(mhEDiR zdm+!_rDUfg20A;617R<~-vP1BL;)0iiXO2B!8vF&LdEtyMMPkYj*~EG+gc42M`uq% z;kp-l9$&lq#UB3Y7aQKonel1mC;YGMgf71S#6vrFJmmZDfLe(ER2uJ!fkIS5EC&WI zS%TdYKRSeLoTP9wcp3onC*@j(SY1V~M@d3aLiSFRdpMS;=d>Fm$p=?wJp9IA4uJrZRuZfuab&#Zn;X$O7R5apSv#nIC@k z^QW?@=H-&voIE$k1Xr`PoXqtE>?jhww9?281!jd2Mt;UR`;WZe0r&K%!Kih zl>4hoa_}^IMCxtaNh3odRu*cRF z*4F0dIP3rR;(}=lkG!yF;m+Orc}(f}i6>`Xuh(_)ukfdYf64!LJ^lk4cQmUZlFF`q z`+3QMXLdY9M9&ZHc;*1B*}o6O{EHS%pA(h2=YdJiNorl*)S`^a>d7~XSgh2Fe3cZo z1TorZCrS)434pl)i6dCfDyg+ z(5}YIE`M&l43CoLTd?JKp*m`V@Jf5#B z(eWuReTU0Jl2hq*Sq9YQ_)i_6@9B5?C3SU8B+E-o72x%NcDXZ=MWO3QK)q0%js!kLZtp zQ2}xW=x@Ym2GInMR}iwf`onV8?%%z0;hq zpS9ueS$x*053HUr#G|#5KrMk;+8Jnz9~~QJH320lY#KV92oeJi9b?F&ios8~LQyRg z*jU;U6CvO%q`_oEF>}KY3A#%tu|!l1iL@f0#VTBka>wTH_c6!(I*0;lH)7;x^;8~Jnqeu)bmUM!8M=u10r&T5sJzitt z@L?42^R$uz^7Pf+#Sbf`uAK^Sq4XwII(%4N;>L7?TfhXMH}DlH(k$l;Kc+WOTol%i zL0{o72pe?Bpwa2FG+jYxrHD#~hJv;}B{aouiyIvsg^!1XB{-}BCIB77?GaQsJ{~$L z#VAniNDt*mq{e%sRfsiHPBxX^eFNOYF8<=pH}e<4UH+#V?q*wmHLNx z0gjhGoXPHbe8lb~>)}R4rVO1{7)IC&0BY*x`S=9rA_$dirJ^ljFN8z&K&2)`!m{MT z=H`y(X1TAWwFUo+=Xa{5#zu@;oCJRTm?%F0{jtX3PbZBE-ijn;UKDEL=m2Sg|2Pcf ztx#y8DJIFu8CoH4iZ?hUI7+JjzN4q*tD6kVa$PxvyZB4|Z$13C{6+v0GC$;fRTlk4 zXWol#`Qh%D*{ouBjViV0aM~vQVQBw`ND8FcoqdS$fsG z|5bJDSk*i4RJ9p$s@{LM`h9%YUj2J~R+dic^L0g7uYORH5s9?~6HBn*`NftVCAf#7 z#D#$kAW60Q$w$w64%zOA5*Hcj(630z4o=I9sQNZ4f+@&*1Q1$pk}*z{_z99!1!Np~ z7&MSuQDIG5BtVdX)!AqS$FoLXVlXZQ@fZC!L4em08O#QyGtO`3>nisB-q*FQ!6Y@R zQ+BkOwQ?u^Vy(^Gsr^;&0q^yfz~wDU6w7%YPqXJ=mf6DESX&FQrt}w|f*+5#8ULoa z|CR4^ga7&;@t^={u3q70`D%T;7>{{c!dV!F8X<#Mc=gQHf`Q3Ld&Fg}bd|UfpyrT3 zGnK0_M+QdHwh0$hQ4r7`mSV+M(|y{PRk&x)>eX|g`f{@`bFpE`n#<|JY{AbGIhNAXHs&U92tjFC*Jf5cUHT;>BR3IYU6)+*25UNi}$=$`~8xb zo$ck<9^84Q+78ahE1s_sf2(CBi{etwu(w!#m6tQJm49&X?iJ6xTM`?dcIA^w+J=rh zJ=BqRc8HvdfnL_D#Ofdqr14@8C-~E})}2+(Q0^O6 zrVxWwVyaSf{(Z1CMAQaSKMj1Nh1C6G)*J@(X1`18_9gu zppuOv*|&iWh1J!CEAGAboi_OFJ7!X)xa6r*^&M4(g;n$=Tl)HV`P5{`lqrtnsnka3 z!TLT{1&l(Vlq%+D41PzB17v8T3seJ4T26ggh;n~}6`!i| zjFJnPPPHJ)U@bHh! zImOzbv0(${9UqJd+F~SK!yofRC>+d*J5>r#1p|q&0~n6JzyKs(`!j#yJp#DH7Ox9I znFU>q< zY109Q_xs1={R-BKKtn?6=NX9Th$5ThtV8m;Fed_ZNJvK^46;awF__U(pgF-GSdB{F z(A6L&-?I?tUFTP~9zG?vM`x|KZao$8{QtIYy%-n2DlslrVnwx>{h(uFXpE?6D~5*f zf8MdN>a4**D6giCj>&g)yepQ*DCe9DRt(qh6|1e)p?15+3d4vFu?@X;yFzO;;J8?$ zwEwf6nc6xsHjpQVqj*f9l;pH~rHPmrvV_HJFie^Vp%Z4P z2>A)|ahB{5>lb^))PeT}OW+eQQ|2vXDg-M@E(ie<9-*PuP!NT?5&R^i?2uRZyN{*F zhoj`fEahaFT5&?H2s;TmSzu`BLuB%F4}FOlC3#SfcaU8lKvukn*0D3PPJFDL0Vd{OZZXQ&{ zX368eRljJJFFx6-e%LD8SUL3qG4LLJry(8@`*`w5p;{c4BU18VD$7*o6{;^q>*4ly z$M6xcWC*qraeQzPtcEBSr4>LyRbb)k7npEcl0;E}=LrAw=`Q}=J<5Ay-+y%5t1}uWJ@T16Nll;rNX1hh z>wEZ_&PfM8*ENPj7e28JaoU_y{Mnb@X7fK<^6IwzH!rT3y)|F8RHZCCRL({*c+Agy zs`7yg2Xe7q+`df!g{a~i#12Xx1N4O-!4wbm75frDEkq6@RRJb2tMF~Y146dCC`247 zVv^c#)Opp{UU9{>)z3Y+ck$xA4?ZV;Qu!5&7hmzv%QxQm@bWLZDL$h$c*{*^-vSq!0gs?T8vb#NP>jY0 zt~vdDG(}L8oSXN1D18%ZBzbtf&)scwQ(~uU2i6Zz?ON~pg8nE8eA#Vc9DquLR!=Kg zfkIbyrnXvNY)biezDd|o>`dc8F7o--pU3Bkor(9jN?VkjhV^~z#!Py@)WP$N@lq?+ zCn_KL(ZLQc9GXC-JBW7+d#X@^a|&gwi8V=NxemRs^bkS`plgpFmraGM@zv3zTYvI& zbE_z{P>t7sGy14nr1pv&g+b0}B>6*?RkX~H#n2=KHX8&O!tNV^Ga8SF1#0a-DyQMu z60*(G)Z+}CKsXujwO&0Xc%xIKe9hwg~r^bFZVasm!A_YG(Z zGG=b@-snm2N|90$LMK=a6zI|XAjuGh9srdt#8cA@!V)}4dQsj>IXrLK(rLVI(~0e8 zahoiCq<~dze4tZ3)$PogHp`r!u!H$^6;gFGE%gf_|HAFfD`QKOAK3ond(^M~X0=K^ zF;Iay`pg zoxBfLuRjfs#h(l?wE)na-=Wquwl=E0=t~=(*)!ysSZHv%`)G^wOlhQNS~J3J_|xCk z&X%BZcAad&QxOwNS6g`DKqZ=R1^FwrS`?$GnJ~2&sRMvwqs@il!C1*rxW=}OaA~9} z2dLSNMV%4u!uT|+AgBT}Pb)f%WrZ59g-}gNwgbLL$r+=8+{5niX1v)%6j6{jIcUK} z140A^N4H}5eU!x;Hq17)T1{H&6If{Bb;4JL?gTgW0F0}5bb+f%@4(u~l-tprVjMQb zXbb8gI_+LgdSR2U_OVG3!vIsfut^88y{!CPh~AMfyr{tnjH3k@f>h`qpE{%cu>?@% zR>@%g#&~A%2ZM(p3Rph_j2%i;%b?|gh#~Dlc{x@MnCgH{Vb>wijX3d8KHz%Da$p?w zdV#4~pPsn%Qn}iv_ zpdsO4JJD;2(2!;!hII3lYxv9jha>#!$C%~Sr*`jukAK?0OdY5!R=|sHxbDg|ZMwA1 z_<%p~Kk!%USoG_xooBxC={xWK(e@$BITqFJ;{X0${fEwql&gm}-n2dTReqA>H1t=c z7P)42=@Dm8436Pg)PV%gfne&U6+`@Y0#k+a9UE77oyiCcAC?Lk$5=ohZ*V7$BgGS0 zrr6Co$RqEEeD5RT7(xdcR{P#M_#m2o>}I=UcL3;*`S*k72=L9Y{BIIxf;Pxa{W6*< z)x5Vb$2Kb@Ze{L{4{2uCy?M*5OGCy57nbTfFmrcZ8K`vmg}(m}OyrN1hn9sMHW&)a zf5gi$2KDeE<>L$nR@3Q#`&;APGy(N5vtYEJba{_O zMjCY>nqfPOcxhiR%j9K+fc1s(vLd$A)dP~9nqq*_Ej`UWJXdz$AJ%)arH8eu$9qV5 z02M1lS2}bo!r)OYZi*W>YgjzH)j!w}2rXM#9smss2?;{MT0Pg|7o<1CEd);*rhtf1 zhTnSANVhs|^ceR;DXw|;<1i7E5*_iV*R5qKj2r8im^d-s9&b;uX_dRRK!H!rLI_kN z63#=Sus`$jAUWl^ORfx(RQ`OK738z!8xBip{`J5sMN1`z%%JB5rbHV@8<>YtcFq9d z2R#-0cqN2f3GrG8edH!O6NsJIeGjH$t6OaSdiaeXppT3s&W=!fVghkZ!#h19h=~&P zRr3fyrZ&wu83t!!pJ@agvMU9+3MoMA$IV0g;gFDXxCISm$iap`6|_MVm!vuXu*9wx zT|-^56yMvl&R{`{dqFlp_bS(8_LW8F-I-8y#&u$Z&NMTv(BAOg+~H0G6#06Us8Kfs zzD65(gb|H`J%^>M$OxrE*5J LRH=$jGupY!gQq$dJ%4iWri^0SAdBP!I`;33ihK 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 E3qeI@A^-pY diff --git a/src/qt/res/fonts/Inter-VariableFont.ttf b/src/qt/res/fonts/Inter-VariableFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3b642151ed40c74ad4901d862a94b985ad57e8f9 GIT binary patch literal 805068 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

ph?w#*#bLAer$ICL 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

u1KkF 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

DT(5aapxSs(B4j;wg+|$U6X_qO9M(0kG zY+E8^Db00ldU|TC-}UL%=52e7B}k$?F{89#yib#o4s6?gf5PP$oy!ya``1gOqvN3( z@w(JG_vA1yx7Zq=e;($~^vSyZcaT(hlTV*7P^b0K&ad%RUj2zo{@lWGYiTcC~K%XKM~*8NXP1aj4_s9MqiF@tbxJ+7x=N^W&aW zYjh1%G3t%;t#V?MyB{0s#)6SNqZLvVm}d2vai2Y-;9 zmSH^$sjlEBVHgoYAjmib&Lw$D99$zNQ;pRDgSrEzbcZ7>IYf@ZC9#E%LE->-R~%fn zd<=H}_xrC2@1OV+_jiW(H=cffCGO#r_W0%f0ohw;l^gT#TJH{@-S-pE?m2mO^pE_I zY5lZ))AsM%PW>NgJFKpZYYy+<{atGiX7nTNmyQUxhr?adh|xnDHHkY$+Vnq(b9u78Is#J964U^OFc?H!45pL<*1a}Yq3HmZ4yfRgCc zx(|DR`-t(iy}5!?sL*B$m2gf3^Gm+ngmv3pd}3ls_PU2QcC>x@h4Zt0Xm*L!8a?WD zH9qlJO>Kuxe^XdeR{nKi`s@W48?1y$g&j5FnzRHe!!|BSpFUe(a?v@i#;Qw^Q>GkV zz59T-yBn)hv1<3M8CpOZ;v71$za7}T_t#p|CScM}&As(f*b!u|L#Y(&m@B%iddDGz z(1&K_-+1rkGA;(bol2zs`y#wrZ&u!sIb#AGc{0-1%eE?u=PJ9M{phw@E8+-6SgpU_ z))44xt;|K)=jjs4tw6D==8aFBEl5hX0`ax+Yii#+1PkPe*7{Az*U*uhw(v4ItO(;v z+rLrquJrHc({9e5?AUm9+);4u`<7Z_T-~Pa*u-;XN8~+M0&XrLWzg=hsl6m$G z_vS&Pdl6kE^W9HyYikTkugTR5ogJ+kNyyqcq}TU<_Ti?DFV&#>V}*S!7$z*MkaemM z_p}nZ23TMi;ZptmL6tHgedbw{<22eh6wBjNOcRip002%wP%1TX$^?yi_{6t+_U?5Z zI?~fKDvl11D3qA6c4!4wM8?2yw;CsZh>cjG-6=*!Ku5G@B3odH!Hz#(6j*3@(8q#< zo}O7Zc~XxIcAMm2Ms0EkrFk}%fc}W&k)jCjll=i4muM--UfYP?5V{NJIIRNkZl$;p z{_-S{dVuQ)EC8vNo$QGFWRtdQSLA8##K{sxwEykqP1-%Eqa<+AO{ZaAQv5Db~V|M)aJ!Sc>r#iw2L#d}g zLy*=PG;^Na0?N4-Jr$dseSs-jci}ZKfo6=fzepKN3qnPR{T@8f{`VIkmac!HYMBFN z&^y%dFsnQ2!?t+gkq+n(=(dXcB6Sy8#KWz5TW*&7`!szx~}p zbKX7JPc)%7*O3Q9c2JiI>)!1W?J;_N!f;#TM=juLesdW`{ConNXh8J$k1r^{63p!Y zC)H);L_n4)wO;^GWiljHiZ>#F_QnM`qE0eG9YL^N4diaosxg;bo<2dFF#4}XxWw}$ z?v;vUIpsoK-Tsgb#<=dU+xP4~C}fqGPebph%`D8!$-DB#C09eJTLX7Efhik+oOleM zE#O%ZzVs0V*pI)fAS)wH?{~XSogNkeIYl`e3#|pSQK+@HvC~DTHp05=9|pisGd?BN z6fasQ1t~aKma*IKB}<4?`Y?_fF z*3*l$C&6K#ZR~{0fC-YEjr@q;+Xp422PMnc;<~TL) zJA#aNC?*7$J@=>zYCfz{{!>pWQy$pO59cUXFD=#bEQNZZ9!q0zR&NHaUI$v`+Ta(HG&R)*EbnG>$}xHUy@y}(GSaf$Lw+mTlC1Bx$6j$$QT0n7 zINJx{ht+H1Qq$9Cq^5#o;m*uonv2y$#!u9WOBc=5da;^K*>%P_sjT~`@2eLr7Rv`g zoz65FtG?3Lz4NtKw>n#%!9L7uXBD}K*60LSrjxB#Rum-}CZ1PvCA?u?b__u-=Ri-K zhxn5$Y14vE)(I7t#KCc@MF316Y&C0R{xA&+FN#3NtATl7M6QC*_jhD>C=DYlOJmp6 z?e*1Ko^JT8{fk4ow4z*4904<$*qh_-$;wiGi`D(h4vLzHoe-L17X(JTK5mqw{>~dH9x}razIBfsP4$hx@tk%1u%u4!u^D} z85t#)V{Kgn$z~PGV%1`?u9Nsk9UOJ1VTB0UOP-t%a3gY&zw{JNs4pnLVhkrtfc~A% zodwkvn_z0p08G1Dy!*VY^X?{JZ<7Rx55a=JN#cf_xikmK8#XEaTzIzF^6-41#pIBr zlMHd3KHhtDqG8U0Yf8=L%F5I6M3u{ZVnSy26{Q7WfRBL{zAxR%8(33hmiTd4RNKOy zm{@W1xv5D}KEUIbF8u*h)Wxbe1RX*qSS4Fht-PSoEg#Dqmltwy6gwhnCq9?1WXz7y} zROtQ*Z-7G;7gg7O80JvlioQ{Bn;L{o2zuQ~P8Sv7bhOyQ>b?|*Zgjgp+wJSz2$ zVj5&7@G^!tgfav?nnJ%3Ew4DWm*#@>$uC^bp40s{Ep6aSkayEo_o2qd=O3}dFr=nT zscYTt*O5}Z2#3*DBv`7qyHUZ@7=5_4`Q3G3TX%th$&BpUP?^NYN)SdVGqRk}?aZ}%1YBI>=3IO96!M@VUS24A&fB$&oMjDePm$N4=2a)F&HTaz|fSi#&9UY zalFgpIqK;Sfa|(XVS+d_mRLSa%y4s)$Sx7{7{&+WfT#1o5szSc)qms`1+=i7zm<`&OoSh;3#lezIS zcL79gCYL36TACpdcUW~&pkdAWx87^`@Do71oF5^R`Bk}K2DF~Gv0CwtS-S2gDg7N4 z<+`1HVP9+E28XSx!Tu8f{lx;pt&BpJ3iNE1P6`Etf(F7uB%a?tXa_T)d!i_0!rZ0c zo+#fH-BS)JyeC)}XbsKeXtKiQ~o9drX)m3;c10N3M- z)I}Ic_#FV@iF5>V81tsa z`@w`Orv6wza7E4EU6yY$gTCm>CwqZwHI2Evfr>3U!%eX8CWHTn?N}{neF1&AF{`7y z)jHsY!VL=d0eQu$nH)88HAdzljLfWpg6koJz#WQdkX>EBtU2ZPLbJv2A2qPz?Jwe3JGrJ z(tbzX?kn?PNKZVu%Oo72L%?AUv2+XZ1hGCN<7^}R%Hp=eJ8EijgKgNlkY6Q3<37~B zwchD`dBtm}23m+vcLWQmnws8Rx3bE7%RJ02L?MODTn0*pnC+r2b^1j`m&5M!62^5; zgxbMdog0UdR6*+j?b?=uqX9rm{OEC|mLfS8rw z$1BwV+=f>dt>EF?&?oA33}WPnH8BW83aZrU2#u*=-rk-b4+cyRbZ|(ZB|-{3ut6^; z03gzGlaqmr@+;5wh@67Cdwqy}ZfW_ftsM@~J{WTPJjVh^Al8A)9YLD-SR{+=5XBBO zhli`HilFontotg^0bxKWdMH8%d6Hf{Bdh~tfOX%Xd7`!Tqb5qe_{r-O|JVA&WxxfV ze{z=@(jmWBzy1mB)ASMYhLLWK&Xl;K+E{c$Y4O^PdwqxkBPY@W%9U$%(`TJww8m*? zTvb-H?$r(MZ};xn=PmooTxJCsIfM;GMH}pwl8*$df7rFho{m1dt+my;1(9cU+q7o3 zns048I}fUFWCyQwz-qO28||{iDSKJu^w~gtHAw@H;V)G9Ut94qp~xuwY-RK<=BkzJ z-fXI=*>QLq2q?;Uc1A{h{nqwF?n`l#altO4C6+;Vgt`d|U0~#3%UWbi!&rj{VZ)e* zZdcmYw!4Q^y7>~x>;dt-#Hyl-E}E9A(;3pT^KwB_HIP<`U8GrA8jT2%28N^(b~ZJz z9^)|d@R>zS z^&rPMgwM*9fV<1xEe-+Xf$5*D*CBbu5CWHn0s|g(Of-rwjEthp+1@s%6L~(gM6YPa z!;~~*kJPVT^cS9ttvU|#FrheEvv(tbnNO65Kj2&cIMr= zb*nc1%5W5=cwM6CfwnDKx492Qdu$*uahA!~w(}q^>-`+ARMhG-Jbwh1%ySFN%NI;w zC=}%NbRP+5P=8_@;ZZRHgue1l;2$a4OJxJ2a{zoB(l|5~N;%cbVK&Ajb}Y?hb-2 z{sNxHGm!}6e27rzb911{}0v;2{!K+-~tEwZ#B zPupLYBKKred&|3Z#l@E;xX{mcsx21FYwaB@yBLL4NIcFB-7EalpLP#rrjQfty_$Z{ zFZF>PFTl0>$aBa-@OWkA%Id$n1LMxS_Lh>{iVO4ei;G(tF>srE^=DlVgU31vMYaK; z?^~w&cUD~vNlX^S7s9$gIVtC$MqwImw9yqXoClo4Il4gi$948CFItsA_c6>LTU^k8 z)ZNt=0D(s7^e8iBoeJ+X8IPfMpcbRcMG@wBczfMPS?oj;k~u`#k_QgM%kiz(54;uY z*mo3jF$TYk;{(2~4tpJpXD?vjR$l@<{=je`q@)1daq+zO+k-uQo}OOha_l+Mi`qLq zeSTgW8>dT1K&30BwG_yq!~2yn3fSO1p5DpG6cj_*eaGt!{(<9Og!zhr-oyB*AHYVG z`q}cLESW07xySlb$*TnbY=zYUpB7kMo}C%(vTsg{S4kfGUymUl?CG!Lko>kkyvn~l z;*|x1K>5^*;uRF)P?K#RofK?0I&FW`2XX>8NWo~}L9Qrfo zX)z-maf>GhJaQog=RYoo(cCN6>|^I3AGicO7VkNS?OX{bqN1#SXPzDg%KP}I^@vfq zPxO}M?d-Q}%Wl2)(o4*jTzc)TWq(8(gi5*YI;%|h>mSQXZ!Ny5_@ssmxp=;pFa(hE2+(H0QzP)jfj&aoi+hl1>Pm=yvJ+ z{ThC_vxo2L9M)<2{jm7KSVQ@xY*MNgxXNq_Fk&X~%akaU5#afNm1<^)B80Y}{c#hF zl&}f{Q4d>t5Eu$AI1ny|r|_%2fmuXom}J$C#VBh`JX2wm6_u4Jqw=D4g&abQgv97Z z>k7#W6)IqEtAz&^)4oEc*%%YYVoZ!hgGT6R6=;N#De~kn@rO!n@(0ckYGxg_c^opr zA}6dFhf72kd6DRU0?su7=Ssl2lsH!u&J|V-bCH>Nzw|$q_b`1#QOf1aa7fYr65hmI zR<5H%$_)a)T~-#{UG`ND*fSif@C0m0AROHqxnq=M08f+SX%$IH%a_w#1K%axI7EEh z^}NtR=4Sgtc?5x8fsCi%+4SlkX2aGMQmd&4;?Qc<_j`b^Np0pAzAbkLpXp13*3P9e zmTG0G_7zYZwLZKkySkKi`AiDNF$G3~mo^!Hg6uP3u@K)J@4E&JGXyzLNodN`E%!=m!v%Y)pp?gQKExT^hkH# zKZJzn<4`r(chu8+q<4sqici1}z_%0H9{a_r9dW(KM^Hvd(8NZ=%g&7;j*||hkN}Fa zQOd~B9JD_j?LQCgKM(CsMEhf7(0>}DilZ~V$<<9%N> z=7bVL3EbOnORtKz-@g2Ewg}M^P!N0r$Ptx~jKn|~R3@xn-`K63aW%x;+}!Qk*RL-v zU2lIP0Vz0?C;>AtfRf$){aWq7fY$zm64Wg~&$1~xJ5446X7F|-k|UefFL4Lwp)VtM zKxo$37@84u1M*E^3ZVE)gUY-A0~C4Q?*;i=SW!$(F_5F5c1}Joo7(^VgMj?Rx|s(X zghRM((W2XOv4B0tbdZP_<*-emd=!B2X*G_74Xv%YxuvDIUvq}~@Mg%)rKKBi8yc#= zi<=l6VTve?Ml&#=u|Hu!)}zi&ILA=5kmo~{a*qGV3Nn_LNi@KJpMOf!kZmwzf5$z? zPy_K05o1XbS465K=5etd$rS00CL(8zl}jUgLY6I-WZ4?)X^!~m=cYZCV_HGdpTl7_ zg9aTeh1!9ZNF(>(?HNIQjXfu*;lDU*WVMO(7q!$1q0wOly?(E)KWE=$^uRRqz(kBf zH0D$c&Px#ucxs%c_P7Bg$LTGZM3zbeej zz9cux!AkQW=Mmb=35zJo7{LNli9FGdEaTycUA7!lzRaRlYhwZIii%b{9+CPt5$(Wg zi`u+${JIY9&_Z6tLwgRg8N#)+OlWBehz=R9UI*E34XaUPQ&lG{5!8m}O|R8AG{5~u zi-SU^2?()@zpG@)JP3jWd2O>`t$hRm_Vn-pZH-tkD-7d7GzT_**!R6YzZKufEh4%j z+8DdOf$Y@b`)(v1PsuYk!ao$!NQUJ! zx+MokIMqh0!yIgvr@BOmWA~@|D`MkCOaTa|*uRzb?*MZmMG*heG2_syv__7Vt3?34 zVKhKWy?m5RTa0uIGD)yBWGLhG7OsXd)XSzo)Ws06)mDH&S5N{&Z$w(BmG}Fji$wzQ z(B@#Z)xs@hX09qSyiz-~pa<>u>4A2)Sj?jx*5wukQ;gSQ=0)pr`62gFERG|sVl1u0 zT8-mIF~LWT5T4mm0=<{w*b3_k`5_-0k5-aIt-;Zej41NGbFDfmEw`9u%X&rl&RLBW zMVX{KM_DA~=Uc&fy~|d}XZ!zm&+)kDc--^YG2=Yx&}Mm{IIRdBh62bn*O^hneksec z(syg@47wo=Ss?~VXGLnX70hErNjQ1_QtQ8u3zSD}X-I9A-oZ6(43C+7$Hak+Xh#zI zkLr++7cpi{)vB6?hOG0?yC658ZLwMeaeN}uylJDC$CNF(|DLk4*3VsC?XAeRVi5xS zVLIpiL!o7Sef7%uu-aW9+1}3&{hD81U2g}}WLXSceuMD)4W0m-gV1P$sE9Jdlui^j zVHBfUhHq?qV3b=Xw~Ff5%|xq=Xq6GIGNM&V1&O^tq-yhBsGiO}*2JAeZK^UgpF2O- zWERbZ%U~UQQn)vtm0HEqz}CKp&P3^Qb`qVbJr$X09gB(3dXI&)Ni);W1}xU!XFugP zxy^(yk4BrvQnBdET*58}o7Zh1hXY{vVZxW@OJt=Sdj#jpv@gT%c1n7PhkWjH(I1!L z+*Bd!m*@|Q9*Ne3`(sR}2#=lY95ZU9Wwk=FUC!mU146*J&tTv0rl@=K|8vL1OZZlY z;~(9yE9L3BcRc5?Pdo+0S9JZNq8olyRD|anZa~+3*QKYR`78z=arTJjj8dxv&Ii{% z;3}wXI5K$Rl>R&gpf~Z9es>+{3uuyzlT?9$?r(nlAgOn0t%>v*yMKCzwYSG-mxL1r#+PJI>2I&C{aT#?fjvWgWOJ>3;`Wx-?Vs-0z8%k> ze){JnOaFY&U;g}OJl}KAk9XL_AR@LkF|n~K&_3kgF(2#`DY-6EUJ^zdmWHt(>=S$Y zMm2`CnP-jHhyzDY`Cy-jI={hD@~j6TTA%VE9I13Vs)T#W>Oi@TQ{|3}(8CvjD$fN~ z&O#4U=C632q-E2BPC^IK<`{O8A0f}??|6#!JNg_qmb;vWGEd<*R_&=Y`2+S72lkVF z!Nu7ZUX-1U=L;_+USuCboeslTzH}-<$}Gx0mIZvru8^hX>mz<;qG6)o?|1Kmb4uo3 zr|d4z+=v`UlJm*++y|!W;C0}i4%BcujV1gClAn)lm5%t{2t>{v;UJQ<@V$K{yRkGQ zB6~-qcm-O-``9*|SmKs$L(`rn!tvnb?p$LYOD`O z)z-rARt`0w-2Si~#oi%GEo1)WC(oG_J|`hVqH{}{nwoO)Uqa{PHfjrOR4ga@uiWlH|NhwKkWX@>d9@B;9iJU;ZMB=#sZ zymIn@{b8B%O=!_{v?v~9L;XlCinf@|KC?K@)3Lt*y1y z)wQjycu&pXERDFhXfmXks>m?jWLKs&c4ua0Wsy&}p5@}=?M?tFoUI;b3qCY9d)l1s zZ4GX3q$RV^67m%5(UM3Muc|6Dlm4pa1-(?2I_RY$s!dj<$z-v3*1E}Z)6m%5Vh(zd z_5p1{H_6XA)ImZ?Sy_qnPHLmeCDjH90Ya!KX)h?s1K6~MW!n{N)@<5j#3JlaE>2HR zOY=MJkH%10qJSr0@cDiIHZQ&i1dK*sn$C}!H9C_P<}Zl?sMUn;8ry0mwHVEw2kn;p zlui*}pw$XA+T^K5V}+LH#s)N=bFZb54F^=AQ{h_=oa|KQ%3(FEhIiFxqoIm|yrP2k z63FKEXtZ~Y(dkc1OHW^nX0t46Fv=(eylrT%&k*oXgN??3PisPRwSHZi{ZXmCgk2&O z7fH0zT*P=QL|O!RRt8uDYS|Ztn#6S(&eD_LZ$jdAOrtthjLa@vH_%Px7FB9qUh3Aa zuC4Y}QVF_*;o$_l4Wy<9dSYXH!nmcWxEfhV<8ZY&P#591P<2mUbpUJ5s;)}=?y7eA zvSW5&^#n|AS&%v8V#h8lbW+SIyP3RJlUi$a*6b? zAr}Jyapg`LvU;XkclVN~`wfPE+%QTAXkvZ7SiJQo z=mz9(rJg1$SUj%tLn~OVv57CJmo+_!T8mjd&TJ73{r!gT?4t4~>0D&_NMW)ckuvvD zEt9D6<6nRMbv%KxRKNV?jve%8=e5;^@PzS&)mpqrWM74OyNS=O$|d&Tx2vj)N;bT` zdC@{tw)go&o!4zwX*4|V^Jw*Ul^{e#jU0%PctAQuVtoEkss?$z^!vx>7Uv=; zPzD4%%ooq`+l>$z3^h-`y6GPuH+}qm-AhmB=UsN~;?h5q{o%Hv`~~)9e3VXO;|IM1 zHY(kXw@91fFp~SmFgZ>fIpIISX~$0%)v8fn_W}2T9>1u;LA1YgqyS$5-fDCjhf(kI z>HWu#1r*AFQt^g+Ji$lN%I z*;AToOf{fIWmECGEU=N2Kd{#MC??g4|0eK;V)!LNoDG7ypq#0RE08M~Nv$o|Q1e@j zdbB#453pJJ?W?1SB`}kT0UEBehv&mXpslAdo_C-fOF&z9fws;!%`ypr(Vm74AGLK& z%BorMw=K^7Lgpn&{#{R1{=463y->fCi~Q|Z1EqUa=tkw5S6--j{#gffL2c`ltgQ2A zdiNgcKKwNnV@vE=`#){@=Wg|siG~T|1D@PmsD*^C-pF25mR@sH@y*vQwHw@B9mhEHckw{YDa)R z`J@vANqW@VI~EY}><XK!v9Ob80D{rkS|bRF2gfA{*z=N_+q^R@c-K5E#EJDi}$1Y-z|30S#9 zxLXyn`mk8*k9;pnvKNCAuK^{V14^Wv)T9eeGW~)mvAf~brnWE#}`N(rCD_1<$;@Z>xRa;}7ctd&lA8$&FpJJMtm}QT3f3vgOG;V@n^r+`w+nNC| zv$2O&e=^U{&b|JRNR74}7V2su5Jl~dUjWjThwzq4xQ1jyrBu61d-EtDtFPAn4Va~; zYwgKyZZe{)leHZ5XB1(L@pg4}4JvgpTJ4A%h90=vLN=uW4w7h16saf4+7PmQ4Gc0= za2Xl)`}p{Av05=W0`*#7nZ!j8lCX$UQ2+c|~ z7<9vjd%C;34g$>D=R+3*w8taamkkzc6&E1a%)kHucyeDxBuyRqG9uTVR?;l+rUC9F z7>ud#W|zRR@-rk&E^)S*J*WILASfxjUxQQ^8&syC{Du+^@Kc3NX%+hkvSxt+jS$jO zjRrJ|OY{CjSu?K#PSK(wN}%LqCAfA++a?5OIK6GHrd|_w&C>S@m2?7VP;JRbE+HUYADGb*hv_*kJivsbBwn>s)q#6XowzwMJ3A2#Yv0_s zdv8ciI4h!2Ro zqa+C>pzS|Z5?TO!q6>379oc@;@#?a3O%;G0imkglAGmvrIHUvs#|zZ^7;#A1HIS0v z45S0m{{w00SD>Ve(4Mh2zyF8QkWgGxQ%nV?i`0K>@_-q6novBYNYUPYBGr6nq!{U) z=EQdUIP$GFZ~mf_73IJGzbX)s|JK)c)X(vL zKIA*nr&S}JT;h1`_!zZ9`6B|+52$#oHNs&9e`XDKmX6s-H0=Fpv+^X}6Dm)-f@;jH zAA1Gp|I#%)@=Sp#MxHBVyO@Bv5{L1P=u6>w!Z)-x*Zr`cDDqRAx2T48+&=dp=+$LWlx`T-jQADRJCk{yBN$Q&3dB>kSC z;mkswK%>EdXSW;AG^@s=ZG^5O36kb9c@!hM+%7Z5R-0J{*||D`tzy|opTM<17R!fo zJRLu^aTeGQ+8|yph8~Gbrz!wP4T%{UnAZ+KuN>hvn{cJcxDsi(B&$;F&qbxU|+{sfBn- z*hO_pin^QUyX~tTz36}&hssirWFO~P1^|2HBrE9%#*sn^4Yl!43;&gxp&&@FTg zArGw6fz0!c7?_#TUEb2A-5F_R@P;clp+W(KU@KQ!>sY{=u#wQ2O&SE3oeL-gwp>)F zRjYM6K=6=}=Y!haY zD6J#XzBidT_A3)ZwJaV8R{{7Z$ZAHiC(9TW>7D!@Ave$q1#Uu;D-`VM;Zz=%@fsBJ zy2jAp>A9vb^+q$Z2&pexUbMvxv#DpNr^DVGrAy4pf}>OLT3$@W@BEElzZgEfROC}W zJ)C8D=s8{iD8%Rw%8NBfMyzvMs+`;Wl`-_ z?E?`+xuOhkV9mpVM%yFW2g`3+xM0D8MT>rQTlswyN@8QSG~@L~u@CZyRRGx1D)!dMC5XNne3t>kT90&wMyiJ3| z1{}$T!+;gAAVf&mPXTm7Vg$;xOp!Wx;T@=Y`#j^;NWJes?>~XwUxnVM{6BY~_oqx2 zRE@87j7vO}3@vAoa${9O@9W`i(|`}&0(H?oo~0hD$}VA{=ae`%)$c05|??m2oqFqQ3}i<8|D`W62I zE1rj>r>_DMT&(Kfw{zECm+Nhc$-u@vn>H8u%&@5znoKi*f1XbA-v+)oMLHh0!GE0s zlhwog%=veR;|wM)z20oT`mgs@+%aa zl{|{l^D=2L2yV+QTc)RU`i8g!B<^5?M-QV=T~ANnutR{KCuCK@*MlOF!H{(V;K0H5 zJqL~sa|&H@N@_|{oEY+ZI`>F*YfuN04UK?2Rsq;Y*>&tnqzH(LQVFONiS#|7K8NKJ z2`M}@G(6nDclQ?uM-Z}Vn4GEwwW*M#NdaE_!$-U8IHwXu0&3%oWR-I0gg+Scb;@^! zOre9rP%oZSYNyOGXHAQ>DSfW(n~}UXyt+nc@&VA~Z$Xn(x#U06q}Z!WJ39#mMRCoJ zULzFFCp5Pkdw0C-n3Z#m@`p4jbi6>e@;hWY<(K{9{`oR-LX-ii@;9aa2u(&vatuxK zr_f~du5ZMcaf!yIf5;TS1~^@K{%bLlEZJ4cjG0F9o72;zI(9l>_cKf;#2ZPZ$nnpH z$y2!z#U6L<-1j3inUo&y-oAbNHwbH|tdX2C{h}iHA+MP`w+L+K5!$#vUHZS$nV@1Tx zpj=(Q6ka?qfNb!md7MIlst5(X`$M9W_~51BgA2h2vrIg&5dv(aA{(!d40ePO@rlS3 z6Avu}{8+EupEPgLqD480@6o!#rCRuSV%1L3 zF)UcH!^%3X4v}f^*168xIks`V%yGSW<3+8C+PTg#*Ri_GybPIkjybM&u5-*)*15%P1@BCiAzwi5tKObYY!$ASpSoREv zj@K>fGk5V+nvoVJUK)AnrC}j8Q(xVG3_VQ$z^lIth4$=$f*y$syq=;n*+cL4_cO4= z?^AOv)8%p+B*rmG84XDbsed%E0-_7>2r7dv*;-NT1jm!&KLJj z5{x;nSu$|(A3hRS8N?fD{}xcDXD9KDBp$j_z9=ECy7J*WqJJ<20~!j1c4LN0LC}|~ zlukt=Q1Y0eyxsdY?~XpZdthMeR(bhQuKLU!9V9#Tt4;TrT`pDTBbY^fMwGB!%u+eL z%HyWGIpKXC{t~jm@2#l{mU*&+iUVxf-P{{^8>0}Jz!dNCz63_*)mC2vfW#u zw)@UaB(OQozVy;di>=4@_k==6hmX;fk-@%-Z|c%;B8 zAJ|tU&=n0>DoW`>cC~mwi^qcGftc{C=Wg+#Nd3zJZnp|F?%LYDNsJ6@#AxdB>17d@ z>h|IC&2@4)?l=5ZXv&4t@9=7Ms%~-Wvx(=Jl+U!Q(y#A2P^>EPlT8)W;X-p`;|eNU zi&mkXEO!6Izocz`o}*SI3^9lzNM%jZi0Fd>4}v-g`Qal;RIdXu(i#HW)LRge3r z_!s<6N)o+@=Pm}KMQc#V2t|wGDi}#39Chz0d$vM7XIimlp>cN2NpMPex2*`Y`ZKJ! zTk1My4rTEv%YB>~8G&bruG5AYxEi^y)!4y{8Sf2@S1JLCZFef;we$_Q_u2OJ8Q5of zM~7e{>VJpe{-dvThC-b`dq$E4JoB?o{3^jq4`yLKrY>?mwF4wlO5kaoMVr^?3RxHU zGVAL4`YQNmpe9>mfS9K_*JS<|yzB$p{A%byGco~b zTQnH22RLCT!GbL((`O!qBKb3t<9H33)@@6{#!}|$R_5wf=1L^1xgRoDefw$f;K1uI zcje{95B~DmXP{nCmcQ;JUL;u)P$|R z4#(-0YZp-+v6YND)i-<*=JsI&v+Ph0npfX(Q}rUuLtE81L1JWI2RGr=G_FhW@}v9q z?dj;~IFOjOXwjktS6t!1$z%fgf9TYiS1e!t&&!~A&k~#*;`JZoqkEEXzc4V^QxF@! z`5zk_QzX!I$4!AvY5XK;g3rw5eTYNB0_b687PyF^)2DmuJ~>Op=36qhbPVLe6;4__T^??a}(`bDWmu=i>3_ns>n2n%}az zl!ouGv5kE435Qm6GRl-;%F@x=A0J8S>^HLVH?s0l*=z|bFI6G+g9rP6P64w|lMP{( zc~9u6rX{3O{RnZ&ml}UB&N%Q^|Fh3Nd^NShW+rFxs=@end-99grq0fVlob1F=9U*j zp}jwQSXJO&>Ql=mBq4K6p`PPcnLgX-I5r`zm%THy|0jzHsaiuBF0pgnWs3Zs67Asu zIGGFT>gsM;j*wefdEN4V0P-7eUsYdUS6{#KCIIM$Ab64jJ#?rG{SR9ASY6aP_k zw3B$^r)#gFlU23LE6QCi*ZkT>@h#aH*x}I<`O}J~=AyS48;L>7lY;y)su8a=cJ$pa zIV{5QQ7t>0;zLKrpnE!UjPW;i9~P7o_iCBSvhG1;M#O$_M`{<|$Xd*2EyT(_e_}22 ziIUy(+>Y;f`FDTo)?K}+u=LX8>ddWgv(3u;cStO3sh4;qHhcD@Tz}4#Z0-0M);5Aa zsYvxTnOpO-TJ{Z#Ch*aXff*FKy=qzQr)5rWTSG@>S~aD3+_OvIcrm1%c|uvJJl8P@n+`hX38vj+1^4DcBpRzzUM8` zVP*Aki>}5K#3zpHYItDq;8CR)Oe-$QCvHqs50v{Ln^;B`#o&0$^pP)?2i|Lz4m|ul z1FO$u$DGX^oy{B(h;C4^>@4APq3C9lIm^@Bcfx(5Z}a9(UuId^@3(FZXRCtl?uG`E zgN8!m&dY(B%ZZZ+P2ZIUSE{R=($=oYOIe=2=l~q323*5*M(F7tlkAWSC43r?8`nnwvcy@_}ytcFQ-K z<+I1s_d+wKL_C2s3WEAgR-#eo)nE?%WT2?AF%+t+YxvyChJQpng94e|iyG-N9!?tB zFUVav;LGUxF$oB^2P)KKra5yyZuDK4vToI9951LXoR{#2?VOckd!U8jB>_@DD8!eM zP3}-fE+uli95Cp@AV1*NQ_;85-bw7os!V;n$`uZMd3k^fQBYbx)Kt_6C~m zB_#&OCI~R~59d46{igoDdz8LmBD+-)eWFWhCi9!tWm|A3G~tMYNkRvdH*J(21x#+l zDm>TnO3N2t00Uq$_!=zL6EbS1Om{W|zGk zC&NRrh*shz+s|r?_qoW$F!2@Y9@9#FA&iv2=wh+*me({iEMR4<(Sh!#x6Ye4L|TiQ zTlCZIj2~JtSq)cOap=Sj|>`-AeC6YQU6flrB7~cIsJr$g~lyc%lb1SMAdnVcS{wrVl)O=5UC47%KCqAtn&RShR7pN&J zn?Ju2vE?vrolwu%R#&_9Xlu*;4{q)3*<4BN>pb-Ki>L_LwCydw zW^!y~WcZx}??*`ylI=Vg%gW2rt+~Xnk;$4!&)B5scy=OLzO*FU=|Ca-=F6~}<9L;m zYg*6Fw|mad^I7_T-Ps2#Ncq0^2V*FH5yNm2$;})d8r4-{UjF2%&Z)Tp^Q@97*>J#` zrT^tW`Li-@)+xD#Dw&lRD2c_wK=xbV2-W+Py`Yc+7}f%p+cmw|VbAnWCuG$&4Y;1} z$g^l9ih?1eKHGu5zCOy!#{GqfF+FiC66x8yKSH$(hr863$Iy-4_CA<&nB8_WyRD7g zww&FT_2SEWLVex4+J7X&bF!T|ZccvH{7Bn3?_CH((NN(3xAqqg#0K8z z?tI-0bt;IxCPj|EZr}8Dr&?6E_?(in%Bto>U;Ifk*%dPAL3^8TDa$tpP`H$Ii8W+g zDo(_c)06djTYFCgUgjTt+RmNdt0GzMjYPBDfiC%WK9M5Eh8NE(Ln`-NA*5fb0vdbO zuaRt;fS6rqGoHBQc7C{|P#t}}bEgXbqOGkhqw+#q;>5c>c~GE=$!c;1?-vifv_CUZ zxNwDo3w{v}n3msJ$p(5J?*19IA!AMBwVnlcErs@WfMz|$F`3-W&vM3_v0_2_RgG&Z zf!C+l56`PDS$8fkkHUk$o%6AjAo@h!oy)H}+vOPPKYG-4-c`%*TnG~DFqfBq^6qPC zVk^Ay9@PVfC>f)zY7o%;9b2)*eD^2I*@!V;#@e-CT)F)AMe6%t%kLN7x%?l%V4s?y z-s|`-*y)>XTOSpsu`0RZ{71HKeT-8-I-sU3`utioOY{|=u3a=u9ooC?JN)n@wDw8f zA8y^Y*JmxfvbK&3R?G@qSytu&bf!3_On1#jmU0(blSd8$EboLPN0C2Blk|>&PTXpr zab6i!#Gu9_@nnWIr@&FnB2S&1XEn0lOm{j8&^RaKBSNpWzzg5HvdlA+^#m>*36`5>i(?J<0#VLP809=P2=N%3u|hwoH@VG?SpLpwc1X%xe zZ*TAG9KCyg%eOy{Sf^aHxW;PH{L>MK;Y;u(gJu-ithNH)akaB?lJT>jEsQ0{i(_}<}UUS7$JqCfyv>K?!X z(ZwipYH{S6QF0s|Bf>NpyvSm)+Dz}W8{X<05xsU2tq`Rc6Ud&4cxE;!K@P`8#!Q7g zF>iE8GaWq`kU|U+=q$+@QgQQF!cBd|rPMjVgm8GG_ag2Vm=$J@m!?%Gos>PJO$aav z@)EC-mS=<*%ur$nNn#czKchB#wa($hB80$wSpG#sowyr?z%?c>8n3Xa#ni|l6^K`> z9i|?w3^uDHIp1q4#ZkfOznYsDaoon-qPABhB-RQCG9;<2&fv3>h6W}=gp7r1JITNIDiV#b&DSCCq*kV4szMfqQ({zv6();hMN6mdQ+L4VH{^6_*ZhsqAi46N=Dl3vuLmj?|w0p?Wy* zEfgu!29BV68XN1|zkmOm?+n=WXtxJ)d7@Y%;}Uz8NUUS@loljU;H*M_ zjwu0dcg>t$d@zdLasQid?kDv$jiN>|X8a?}_(z!W$;@~I zd~;{>wrvX&I}xt&mmZTt4`+jn3auQ6P60}pA@ zgzhupA)!anH-;u+Bfd({mjj2)MHF=j0V$%}6!z9*8q9M$7XA88i!n(ED(N`0Wd)c~cw3ujl zD^x3A>Lecs^U%7{6tPz`LmN$Qh~REw;l0T)D5r{Kur8Bss#FywJ9j0^xq2rXb0>F~ zZ&am}KjGM6D&1@p=rnCEUCheKxl)@5dG1Alf2b>>vYp(NduJ3)*R8le@))y_u-uGR$xB21aJ-y-UXmNq^ z`23S}>#U1B({T{9+xq{5$3MQSa{x#7c(1Q86nan9*E2=!&pzMT**#{vpgv%6yDzF* zUjNTGr~pjGCT6>dd6we|*xL>yOxP1>cn76FyP+z-VLCVCQw_}~jG<~}!`f9}zH80h z^~*nTLEs)!B1yqppUS#NMb~G>O%}u_l^?vwYR_f2jDsR=nP_+?PltHh?U~AkUyW6V z87p+abm}%oNl~`s5{YM!;pha$XW&;Oi|z4ylx=;co5a+fO??Uit-MOGjr%M|+!`X?rd1xB{lr6+iIv zHWt~R)7?EF<(`j4RG!UWXelW#SMgU-JNpAST;8X;X=UF>%R{eap%jH>6RashrlJRpNt61=f#u@D}?;O2Q~Fqz}*4079%v6!kcHoLATNN_o<5Ex2|3#j17a{JAr zM8ipSstxA2vYFw^Da0`g9hMc(G@;12PocVo$5Qc(Vn^{LT^)a`|KR&E9xn+w$H6UB zAmqUs&V1t3nc905ti+Y9gjnAs&fs!Z;;g6zV~K03CHnIA_KGXC$gAJOXS%gJ;#gR@ z^XXp`0qQF@gTtf<6mx$A2~Dqc#s}J&t)F*yzY{#mkr*VY{-K1uqL#dlM;&u-L5iv? zb7(K_s`}(S7(ehMAbZ<^z_f-?sC(}K z)*I4y_ftMTKD4(x6l&<{$?)KEa3#0iA)_Vgt2g5g2rymCHX~&Q$uC0Wl%nYu2bGDk#^dY(cNg%~?ssR+BByesj!lI&xK1PYetU z#aI<;+r_yVrk4E^WGTs!8rO6EC?aq;33@e)IpLcQT476J4#wa2_|U+>F^eraUSuyW zMw(YlS?@ct*ugD`Z$Uq*fqpahS^ri0{I5R$%hhtzsQ2pB#PtscoIlzuf(6 zbkR7=cufrrzT9%jyAqE>WE&c)%7T-ZFYoMRod&J0WwrCKywGzYNL^KNadWdTw`UaY zGgEJAp@r>T!RweeLAfE~8TwV6?9L8e0>LpD=3)NIdmo9yo0Rz;O?NnaI^}S6lh0y5 zt61ve2AkCQJ>EjfIK?rL>WNG}GRidJvA`{$3Cp_FxWosi5y<*S##PO@MB9FGzoX3E z^1a=CY${Lr-060^Z4#TlAbVodx3skUsJkbQ>L}jR(Zc5cF$(1A@%U?9o!j{Bw=s{$ z0rHMnY^R_AA{NKuO-(PWvJ^e~LsP)x8hO39e!1H{CNVvG4=17{Ln(BSIMO5NprN5= zuEz>Tli^v^Fo71drR3u&!u#G+@p*vma$8r|kPw@A%)kv=330@Biq-3D*37fM!DiTg z(E8Cefl1-8hJWTbNeVsHHA^m=>nUB(a1E@><0uh(LCkxRL!M{K+nMx`rL-_QPjzQN?wV^eeJf4pmFuar9j~l#kJV)YRkB8V-boGLncc~u)S{#!9HdX0f!wDd+ zXO`F}WdBrfifH*vA<6dc{^XPwdv2E|X!wl(D6E>;D!DX6Z5k z4m zjlN8k`+QpEl(9e|TXXg5PgjWz_fpW)rOvTXz_8Tix-t$8yz|ZgUd9*@T-O--z|{D4 z&<^ZiYW$LX_kI{=D+QkD44@e-fV;+`2is13WB9#eF*K(9tT8J@u5$I5->O-0{Llq7 zk+lSoWfF5S#p%p1vinf7@;{F)E*Z7&w@_M<86EeVPw2U6&QH7{OPMbmG7P^fiPtc< zwl+B>X@8xCj3mv?t`3Q~S&c81PpcN^NVh9!s_qM#>*`j87b3>ATGoa_jg12p3!zsn zUXp-mJ6Dpst*-sPKF(iTVZe? zwh&<=TrnJ?Li3Aff)8d-Y@F%beMVkmS1K2ChS;oG#|;^>m)J%dkXP9o{Pl9@1`U%w zn;GDN<0UDZwDSqZ{Rzf>6XO;S%K3~t->`x|`mIghdZ>BR{p&V9FrhgGhu9OL9biOo zxuy3fK$AFzYIefc&A8jZ0*?7D?!}P&>QXXsT+N52o?yOVCvW&d<7dCn*!Yz-Ytotp z5NbH-xt@#Aj*hvPfdL4@@oU%${1i*WETyvimcAEZ_@3+WSq7fx!_(bNTaB^rQ#jxa zUPKgWVagv=;{l;%;~OAnZe|@e z=V&}B7z1=GfAs9h&Y8I^fwB}h@~BQ82&(Z17$@Ad8e2CTpwm@WuAT`)r;Vv`32#*6 z?~1*HPl@-!2Zv*bhai00I=Ran5EO&Q#8|4f>tDp7?8*NVeh3B*J!Sr0Gd}-y94aZy z*)n?OBzsYI;1OjBSkpK(%SV|gAao=;a7b|Iz5ED{O~L45V5<_?x)InCp7TmzYi2|@ zq$xeJuM^L@&V7;WE0!-`KF=E2+lE-xwwDx{zUgUsDhZ!vR+~2c7>Vdw;N#j7b>OK@ zftgMvBGsUB&NW;H<~p@kkzzx-bIwg-q4EgJFANww-(+cPT&-Gly-W29YrC~qxvpOo zFni`!-d@End>m-l%PjP&UqPvZmfI`mRxDYuT7GJV!FvJk9D(vA` z|GgP5O!Nr{@%z$`4k=`k34OxvKF}vzR}z@r)6>wf>h|j^tCrM&(qFm3YrASO-L zBCDaI)a|}-QFV2=LIpFvu&{E`^{bW`GS~|SrQfca1+(3w4@GP`Y8$hTPJty!>lE@? z3YebwSbQjg7JGEeM@X0;)RF%F1A{Ty)6RSd4=qp~M_8wEym5sOOQ{vi2w<~xn@t~2 z=)*C}C!$ucWF%tn#2M2a&VrL_1w$Iv3kxYIDiX&@)dFe`lp6-7tzlJaSrw^=RK==P zvMT3_Ok6h0Vb61%${rgs@$-**z20v=ZwSSmhEVLv_UDjV`U9aj&k%~QFNyd4drB%U zF{I)!Y&w<>k&4H{C#B+({BP#xD-5x?!Vru5M0YegVz9mz7h-X)h{ZoTpk{pvN-lD7 zG*VNuw4xk3(r`VSqIJR}Vjq>(I}E%ldnb6(`1fPnID^*JH&fW_rmwI#NTZqSkjV_m z7-eldA&Q&8xmGHqO7PEu8Eunp|kJj>DpwkUME0HtIKt8jZOn zQds0;J(-AGKs9=xFd7{o@8eLEGJ{d9HPPs?F`o^=>Cn6VeFwtPSd!X>yckJkS?X{lggyi9KjJqy< zvDhjZcMy2wFx^@ibc90n^$nk?uDI&j8n^rW(j`LDs(t=UY<t147cTLc- zl$7=|GOloie>C>NPbbu6KTyq@$DkeNdz4vZTH+wZZ$9QICN2eyC9?xLT4#{3b?b6s z`iaqaIE+C1egrBnzZr6xIfWNK#R$cV^fpE)n&oATa89$UAl$c$QU0*AUHk*S_hRV8 z9QUS2o(Tza2*WfT9^TsDP62A4y{xQO9P^y&b$Gu&^bW-Qj(2;I;96RChqLFB)63(^ zW1uqva~k?1R#&Be@~dodar4 zS@~s)+{M{``((H0YNWT1I+CKA_>Pr z1*wH}XF;9kSoPc)w!%#Pgag&rC{Rer4TRB{agQdX59JNgK8P9N8<_}3{IE&)lc5^S zVG^xFmKM(u_BC$J*f1v=Jra+M4#!S_S?rlQ=2rC5x=vyc8+C&$215D>9Xf%{G4X7T5pgbD#hCq4@A zLw>Z3s5-8ZWm(|XjC#=$^pdC7pJmIR=A2gGP?qE9X{`J{mipbyHcF4|VqjE8mfnR# zowu|Ag{9|tE<~Te9{gp0P8qaHbq3%TK@P`7Ox(^0p9&Opch}dW4EV$A-G2bYfWVJB zBHfaKw^E!qZ4e2k&ouA)31xt~9#`3g!USb`JXKZmOH0pR+F*RoTy@LR%9>jM7~yz| zP{hCk;TNkXSXdQ1b};;>Apn3Cx{r7ylP&I-WhMD3{Dw7eQb}$;qvN-+3H(o-7xt)B z_I`i|4j#s_X=7PwQ*TchNF`{L2McDcUft8P#_bM)ID!kIf}4X9jbG|9q$pDj?zfQH!sINOuxv~0`MEslJmyejSBa@8m{+=~uh-Mu+v{oB=<4lnMNaTb ziL+Xt@fRVDB@PyJ_pWxA_N;QbLyc<&N?mp6m;6THyU(~FKsxAT1-Q$YB9kOmVd_r4 zmz9YV=~e@4HG!{!UdkEu^~HF_(V3Mv1!FY<`$4L$#VI|*ix08BUKv4?Bs4dmU?wA& z$q1$}0*U9BN(Wj?H^KaC@L2Uax|`hdJgUv-RlC{EK2<9!KCQtMCG&!6wyy2>tnTgX zLYnZ(!h;Lj_#2d9r%k**jx5%2FFhh5l^RK3f%Mt}$l&w0YC@fOTN#VAz_G!>V9LPo_pteV8Cfd)-cZzMiBrQDTa-wOsPR$A--9yWn=_{C&S`oHbsG6XJ<5dBW?VA)r)~43e5k8)yD`U$W!l_M3Q3>W+zvMPnU{lTf|&yYZEZb0 zl8zfwwVCQ8Xd}(FP!XTG4HObIc`6qhjGZVVdsIgTOaYn*--EfNjkD1kQWN%zQ}%;i zGbO3RMD;uD4(WVq)~{sN#U67dy%H^kM7(5n4v~pssFQG7%IPInS-;acz`i~tCzxmE zyj5-VfudzM<|hbD+qCJA`uTL{eEpA`0-5ExutxUsn-{5|9M+r5Cs77!lH{KD%AqZn z)YC+UAbsXzqmHdvQ>I=)HhD!|uqL3n&;9fgR_B-MI2w#`IG|1Xr_Y_g_zQBf9do6m z7X2xn2#uEqsLTy%fnb>Y==<@4V6H7=X2BsR{I>D%+hZzgildlkP;gN*BOazyKa^uc z|AQXT3-+fD`Z0?#+no7XY8;9AxH7wqtI-%1Mr~x>j$}wFmLsWklziZVWyF)_WWA%| z;YeQIK74?ZpYf^(eBM-S>62>K`mEi%yCRW(R?f}JJ@#0qbG^bJ9X}rN?h!A0H3!cb zv(s5(S(1^~vVCqFYX)#Z_mM2}t+Q6wOCtKP%2OOOp-m?|AXxqS^ZOF0Po=0;6 z-Z1alr3QT7+~$sco4c$EKw4Z@R_M>lp5eLT$P zeG(1R4U5Vb3de7rKVR8tp|Qd}exT<$Anm!Xu87|<+}E>bS7%=ZeAwExK~rhP4KM|d zsvm;XI*=vaM$>%|&xc)n|ATrT8-J8@gpa{OJf_xLRuSt}3K;saK5E9!7&BtN;Mz9M z(2={aM&=Yc^K-1WtbCg#3q=S%yfB#{3LoJRx&=!nRie;aWnkV+#Nr#hR9PoKy5ZRv zLoBfPvcL&M#TAo;mY#L)v_E6C6`8uKasND|#8Rn zCA9`IF=g1DP9is`)iW|4ywIt={c3mPm#%ak>gebgb@leTG)((Z=OuM@-QE3`l8diJ zHotM@%9~h%YvzDUebdaiFfKsI{#eN|1U+mQ*?ljt3>^{i6V4d{LQ$_-r#rH>psmbv z*>&}GpZw^&kKR!K)tiM)<7kDZZDHnRU5?ei74l~Sg9wuN+R=u zmao(;E1$#a+ymmuF(L{@o`8R60Z?-%V$spyS+O{5ni@YMB}j$NqdJ7A!`l+QpT%sk z`N-R9#RQ1v(~?6z99kQT9$%*=^keHV+W0eUh2n@Rp?J2;V4*EL6L|zJR7swd-4(-R z?bDBw_5eF68uT%=Xl6edEp>~-8IX=y*PAd}DvMZvkm*N|-~HyHXj)f3m(`ui>PkKs zi8m3?l+2QnicrYb1mOT}iDlJl#H)I7&ohlf;fM_bqb@-tc&QkWDRGBO>HAXp{xSM~1$}pi2J>f^Hb3;U zPz*6)IP}{Wc6iF$+VDo%_Jh`zN8r)E-+{#7vm4;Z*WW~naXqN6F8A41uV!0-bJ1x; z$Aj*&;*tHmtM05T%OZ-(;hA?;bx-%omDP3iH{V>}_+@z1TNgpeeRc~1<2=y#<+j%; z!28pVkYo^dyJxsC&KF_`3FefRUXFZn!*vx)ZdrAm@?)ZHLA~=3^VQ7IV_@b!z^@gw zAa0`1!m0!_R;~Ql90tD*zFYXWO%%LKtSgYP`B?1O-hl&0<65B&@xW(~+X1T4T%bO8Y~ZQpKu+1Y*%Bn6mz6CRPQ(@&w3U78OO30(_NB%<@1AJF_Qj>1VxdEt zyL{O{RVc5%>gEL`_x7DXa%9{wUqU;Qk;pIURkXIY3iNQ{Y*%51TC?Vs@)Au{Lw$~U zx2_53Wfxvn{pB0r*0v$~N)_gVM12Yx_#NASK)r)2eG{AiyP$zKUPMP{H7@^HS@{*$ z)QS5%x^@m+_sy#tZn}a5ZFuI0G0lvT02%B=48sZenLu)=*(~CMZAO7POwpFOuOJbR zC9`caK?0dm?L}7fZobKe)-5p>9U8_3h184Wy^TkPXqf5-qSP_>1GCaRpLGjc1RaE- zNYyQzE9uDUdy8vw~>EZcd0vNyuqMP1>xBj!-5HWlG+MnAhQ6{Qg1ZKCzcc-6*hb#v)9*n z=D+gHM}flzp7|x7IlnJzXS+>vI;ZhAX{2xWHhhxgAM*Q?FZYejg5rzoXN+2Y!>o0X>?}vcxv$(FfZ30g1m9Ff!pk+x{ z`KQ4d3s6R4M77wQ00>EXV+nuu_y}eDu!Bvu>An*2wsGqF0nq)r^ODu&Ue~NL~sb9JI0R02OJ`qP*w{i z=*J>Je@33e%DyEVRtgJ#2I%`D(6@F13$6tE%6huCKMY`f^VvP!LzAFB4{Yl`f-Ep& zilgLgs8GRBsN;L!#pioRirr4jK|zC-@ubx;)0OWpjO_32eIC5@s>gF4%GT*6&TL<9 zV`Hty5y24AV|8EC7%-Pze#4g+sh}L^H7uUvDIDqvHGZkmlY?;5cPKj82Z=iD@KoF) zsuQ<;aNht{;+5F!bX$QI?t1xczn+b*f5}7}#m;Tpej=)}p5E}dzH#H0uKpYtxml6! z){TKB;JsmBXD_&}MR{|V&&Q8*L2W(UPI>tqH*v06ts}LG7gDeeEVvUed7FC%)B#kl z&qKksDEwaO>!V;%`SlQ`V-w?~dpR*K&MN{aandj_yEeN8a{(&{7+Gvt!D-?cnS`wc z0HP?!DQOms#&j!23h-w>{8Etvd=-HXDokLso0**%NB0c}$zXwxgrHdSiD+ajV7BDh z?Z6HERIn^^JZTz_MURe>4U;RC;j6E_c_2zC% zuGYL>uZV`9N!IJptP)Za#z(Ag3EE4`>Pc(YqjX)KNB7UTX2scI^W5rq#8t%B%GB)PCCEzuUjrnjo zRdHq+DIk!qCG&_X8mEI1OKzj%iD;6fDFi3&zokm zm6pwLXyF*Felj2`ktW;fmEEtt366jB)$WyO9h$txFkd?mjlTN(!N^EK9y5{`Al;hf zR_EAQ_Pj4jYK&GCPnwJwM*YXA;bSPvrHTqB4LhUE$j~&{7Lq!SWY?ZK{+yINOX{H&t zSl?~(wkXp6Zq_#1_(5^r?7a%emt(b0Yp+&n>?)(5!)jP7E*>N91FTbA44V?T&u=CR zf8K;{OrBLZ@hqXFlCN^&S(>vLYyVxz7PV2HMed)8XW@buUA@|Cs%~fSC8Pu-mDN|VS*y$B-C1R=}zKF677)Avpn%6y>wg2&XC?&x~n|>aJ{Ft zzsjS}4^{0}s$wA$&~8)38c8P6qOHN`cwZJ`sBVo#DbLE6sKOUQ7dh6KdRi_|%jId} z4}cQe&@QIZh3+f~X=S@oH3hV)evh`h60eYE(`vwAlb5D9WJF0+L$ez)7^#&eH>4mh zJp!rGeC7z|x6E$|>6@LQll2hvj)sngQhjBA=?--j{oVnBYR;%#wW@Za`w<=f*=BTK zSZVZY;&C^@ZDc5%6O`g56;sT->x4jrd_f>Yra9Owp1G z+7>O+<+S`i#H)9a5tDOMfq(O}eVD9XZQt4XYR}$ZZ{K`xB@wH)5DV9s{3lb;yO6k| z%Kr%-z3KV3GKU!-wmzbhPA!mVWY1Wo^j)b{3d@Bh#;ST{MTO~Pb-l7{($7i5LF$fO za#7VuI5^|lbn7XY>6B~dO|$2CkKDU238hdMJ>-tJI+ERSFK9jsNdsFL}4v zyrnJ9w*U|SS6ZBNckkZwT7OS>=kwdQwzb{64Es|9*$1jfWAO3hXZf~T-Vdq>Z~aQIJPHqj0JUv6p6U`L-yms9Z7CF`ETWdy86?(~AZ2qf8f~hCm%GiNq<0B${_nBkrspHQ4^s1S+CE6roWHu? z40@2FXwo^R)41)=sL1K`piM6PtC;$~+pk^ngg@`sNerJJf4X0(@o$v78ctG>zdG^< zMrFIo)P%&oN_Y&B?EZpiWb6mAftRb4zC&!_Ql&MslXRa>!{nW^fwvkq@OL`X zNiNbhaBRkYLRPY@EiJFBGL%+j>h+euRJR3HK$iPTbf{OlvoOV^KF??8`!Hr$48ym= z_^)0^x7(}EtwU6=NdC)|ZM|9Z=3SW8`;@ok8#{Z)XS&_vy+3aOfo%{6jFhE2p2W;O zZc*b{JN$WL14DS_PqyV}>nFrtMqGg~s2vxRIA(5>J%;aO5*Iz2uocX)V6GVR^zRa??_?t4i`o3(%c zfp9psY?+!PE)4B2Ti0iLsoqdjUQTTbFFJ#w4VbqHZ_?IJNTdsx_XW)RM|gtF`$f!q z5t&z=Xd2A}zuvmFtSqPhr|7-E-@Yql-QKnhfz@kk*`1hKwiwklzEzX`)?&}RC6)7V zv<(*F39|lE5p3034z1bzsdxrK0`?YzDzy|RL#5g)Op4eDojmGe6 z6FWzyZo{z*i>4hFFxFaK7!=GID2$28wULTyqaD|mOmReuN|0=lb@N-X6k;U69!TR^ zI3kFN6CIbCkfQ93DJ+z>TwljbiRHSMnYu8n%_O*U^KZjN7?|=0N=gQ@P;2Ife@n>Q z#V#$pcPDPhPw(9MLk!r@5;}!xIocO8>{>7MQ5LftOO$diVj35_#Un6>Ky3#ej}w+_ zM|_~Kw|0fc8sWMeS5J>iL(e*9zi?Sq@BX9qk~vqPXRTho{02Nu=FJd)aP&#ON$y*2 zx#fKOVO$ECy{z!>fAE7J^vW;(oZ~|ni*W13yl)C#xUuPxE$utnzR~pXww;~74}}K$ z-e%&_9NTrPa;jGjvIO`JdzCB zqr=CE`y3s@88AAA5h8}#6W8 z&sJ#mNp&_{6o_He{WeRXvk7 zEu!jQmtiJWVQg#zI$$ab#FcrNx&f(uH5;fu8>p8mN|L#ej1#KBuv1%s;2=6(tJjNS z-Bi|reHys6U@|FF_TKdc{r$Tgj!127VXouIZTH5Y9>8x^tu>b+zZG_YaX7JVuG z6J3sBi|!uGo8@k9{z-Qf#r{C|%R9HZ%dkbaZEgRzmPa4PBJlWghAn#JKq#~ePl?|} zrT~;nYp_LA?$?GbJ%ZD0uU&DqOGDA0SyDQuthcwKVqSI4(uMPuTnj8! zc*K$LI=ZQd;IuP&DZ&m&WCmUsQbZSCz_AKi5Sle_l3(cix> z)Qf4Xgh1+8Ogr4Q>&b`wM2n8&M*z^^2=t(7G`7L(VivGFza=qNTCs#J;Ywz;;fH!G zhQlhV*C484-7q@qv19jfv)EU(b#BqFqw&wQpOq0GiG|-IfRLPT=m^0GV{wcDu@T^4 z9k`ve45M-*Q3F^uCpd{uv+h!@O7^g1osvC#LFjE&jMdrF*Ynf+Hae$ihW|6R{HA-c zjD5GG=O{$zNcVH!+_>qxzf?PXzVpTji9PT>{g~!<&&TQH5=aY_hT?Q1NhQc2mt8qK zG4TB6zygmQ}Gs-GI+faM?MaY=G zjL18OtqU$%H+?TFn9=KuioDjU~^xRY*I6oM;=Q}U-M5mRNT>|t@ znpIjl+SBtcf!6Q5yz>Y5`$VZaj7=XW4{ZIhFl$PD8W9|^!|%~z;=~C8J7lV{ zWMF>L@m#R!;;Dt93oBk;aI)11O0k+pqD0&&X(EYFu@A+z=CgB&6)&+P3S+6VNq9oa zzKwS=9z#Tp5dS72H<;-N$;9%4VMm`>N2&ZY*_EoOEu4wyw=J-KnweTS&T40SaJN?@ zQp0!hWTY!nQSb3;Et{KtSzb0!c{##7idnDVGN0LQc$j9!!x9M`HdNkVh1I~zYT)Jb zz{?kTj?|gCJQTKZe-<>9ur7T(%ALPJwPx+g-rB(UL`&@t%fvCy-I zX&BhaA!cJ3+psw)*&$|&u~Q<8PQ=C%YU|dY^@OGN=2;2+%6fVdC0D??Tv3wf30!Vh zhXFDu$J+o}MA?nB)>l+lUj}$w?6PZO(jB(XTFedS*u`auipP9DU%BL&%IXk<@cZH@ zFq?&cUp$1S5RU^;UG1_xA0^{TYwPd&N6YbnU7NhS_}vTv60?2LZriZ_?t-a$yuIx{g! z1lXEoRUruh(E4alc%#f1ek4{!>;mN0Ca$PlkdP33o6lq!_m}9A@R6}Nm1gs(9y!_{ zAHmrv$B~yq?puSdW2MU!YLTzRQ_7?%3*W{e2V%M#i;-fMaA!aEn>@pNC5ZDYZp}vI z8y@en7I-DZG5MH$B?%Kz8Tl67)E^l-hKbi=#d{Y&?|x93gVEp)fj7iB&J4I-Xqse% z7{}TNY66f8c-{zr4`wRzYe&@a6rB;C;TGUWV#8{IpHBimb9-Oe@hI@{WOvW#ImD{| zR1i$bSyM~gv&Fg_>3vDy>BYW?qtsO}1j5Or$Cd(TiQPZ*o!;J`bL-0h9-c7#BW$+5 z{@U60Fvi_i#~cgl19QpjPz!EZ;B;Bv>Z!hAfeQ*5I~ddBl0stGUV7=rsoHdb(=vE4 zHfffDa`LCzV;y_gb-o#M!1jrjmVd*iplfd*PHvkw|M=B@4PVFngQ2#~0gvb63#LSS zU;LIoc>i}{9Uj@V=>@>ySG#xrVKAO|ZdvJUrzQUWt6jT$hT}PP!hc2 zva>W59S#_gHbkcj;)xk%*|^1tnbQoJVzn`Ln z6lBs{&wr-3STq@7dYoHRch<3~=bWcyeYg_6rLg!Dr>(6H9^t*PX z?yCM3jiKn?vecFHWp3{2x=l7(g_Y|abW4&SS zZStIokS;t%q`_*c;Eh(|+>K;D@euTGMgbeNG<*BYC1|U3gZ^m%??jVrd;BlV1}4%w<=`%ACPNm`SZLDHTtf1eB@Smh<-kV+3PfmQyNEOKW5Qux*ApX+4* z{wi*TGD-E1lcOQvZ{qs2rZ+tn@w`oY@wm4+cXsQY?p>afr$Y1kJT|?wx4c`aDkEaW zRJq#lz|vM5<3JxLRiRK zxrD`%hG}*~hT&#|Ur%aZrAHw3-o?A>)Dg^Unb*>-w>o;NPL80fx~tl$SN6L)3@;u@ z72RxJxq9_V)TEY3lp?orE47OWh=@gfnuM%QV0ZD$n+?|{etBo}oLR!tIGr}bFYhzS zRVlx`ui@B&U!Khg{mU;Ync;TlnQSFR#U&0$@zg*;IGkCs=FZ!3>iyE%H6@8KFCumu#D>c$5W#y%QDQ-U)d zxn+pbkemx9iQ^+aQ`T7O3O&<8f+Q>6dg%yG z@x+r6N(Qg=2#tUZTX3^yP6?XTsk1$sMNx~-FQY3WNPE_Kvr3AKirwy+MS+6)`s(tY zy$4V?y}h@m{Nwe;=z0fymRDava{Wk|&otv)eDH#n&w9A?DpF?$@dAAwBq2k6U2P$j zepDRjs27(-&&;C?FBAQ)`1zvSL%pj9Oi`JE1R~~fW&qzwuOz3o#FK0bE8!^?3!Z$Y zKt?9+S0)?{$C!~*J#S8@&L40QkAEl^k=}c!-?c?>!yMUr+y%ew6Gq$kb2_d+{jLu< z0q*(`PT<3z)g>*9g-4zRm;9fzq;8c94Z8C98FVH6l+&@@Ho0DGYHH&8li2RVum1o$ z{-x{FV^3kL-O_$~6zQ=~;Gi>~a3+2;p76J?tv=cNiE9PhSb^s>cP};+5;SO9DzdWD zuTw9Cl6mX3aC9KBUbAQ;f%UqZ=w7F9y#X`XD%FCFB^_<()wVpYs3OcI<~g&~b@D!h zASdsIq^maGivS3?7=gQVFHUXaWG@ya2BjC95fazyLn9=v=3U1wQ^NZ!axpKZIcY_t zPZ`Upt(f9_NbP&4Z@sA;HET6@*ey}%h`!&cNk>d$GkjQ|Q!NOWer5|k&5cQCz3w3! zL<8?dBTMCpdLIc0p5=W64@`Py^g!;@#|9ZtvX29Io`OH`k~-5WSa&4ILO%gFMXKiy?~d*h3Ja|1+~# zpLED9_VY}cMWY#gEpFw~%q*%Y%{gTZe?A8>^!+ji^D$b>92CmpF$eu@ZJ7fyYE0a3 z{g62%cp<9VxUd}8uzKDLNAgr2?-GqQ%)4Z@)6(N;p@nv4^$A8*$VzLwc^Y42Opqnmzvu6)JAkoNc?GG(Bs%VK%&YI+1`XlXsJOb^Ja%Bx! zKWcwoaR1NSpQKzshTFA~;3D*DySsmbdgwRZyQATw{z*r}yQ4eNFzt-))@fOvXLIEG zow*L%yo)ZwUU1n(ws~=^P4Rg)D#$lhZ4fI})fYdG1Njr^G*U^``}y<)QQTj=y1Y_# z8V6r9zEZ=#uPoxyEvnORF8XNgmls2~B*C{FkLh2oy?m0IC>E5px^@A2)RG0YcZj3z zEw?O$J$%$#p?dL^FT8{>x{o+~mZ4X$JiIdG)7_ky>C@k2ByajGj*m?I9)5)rzvK6r zAN=0C!PXIakGO7U=6j)zZTC+^Oy}RfE!6Q8X2hpDLhqpVduJ(xrOxKXVn--8iy8yYf00 z9r=5p*ep;SI0J@D&V=FrCWxCcd42X8gwBnKr|D{ZJNR@zXS}A*URwz!l%pLWznM)^ zdCN`w(vDOUG>(Uvvo`ewjtUlsvfow9WJOhwiX5 zFZyrpF!ZNl6g54rOy5m!Fm~F>J$CqT)K@5bESWfMkNK{Q4jpC>9wst5!@}=ob5teR zgOpW1&K?~7n|n|fl`Uyc*rp_8`i8Mv_^`p~g^xHjrBQeO)XjP2;NYP{)UyvC8XO$p z{XHUo2aixi;>h4&|7&~q_NRU)iBsx#E--#i$nSsT_rJV*vyI(5G&DSn?*zR7GdyH$ z+tWY)`HnVKv7>KiM|ZKKTiMYa6FWM0uxtCbWk-K|d)Hv@DLXpU^~;kx`oWyLRxUVC zsL#1qNPQ+!*vpRo#PYfic69y9>;JADU6*Vyl^Dk~lZ8$3vLb2W#PI~;a6geaQ%xm^ zV{*I!xe1%xsl$i~dsA)G1tW-5cqV20FJXmq{vzQ!9jD}849fCsB?`JWKx1 zp`5?o_J>pR{~kj6B}vG7c>OX>@^4sJvG9g8$-iagEq5-NAo)Ls=>J@8`EZWnOnmo& z-RHyb7Mu3lr?Lkhin>l=A3pXWs0~*U0@4WH7WRQ`oK_OTT2CC09TWCplD#lbeZYQ9 z{jmEDV^kchntYu9r{S=NRPx)7aQOZA4~4@+@8i(GJJ>25KI)sEJQ@xk{9}*!e9P~H z;fde5fZzR@@-y{H4hoM#iI!~PL5_ohj|&fyO?pS))Dy=@TKD0%=caGRxE+E0KOM21 zittWDavKd?$WR~(DIRG85KN!SGV*Egp2XEkLY#iLZC9Jy-PXD>czYt8h$Os8 z8)*!B5?zU{iJ)7>$G zRkBfoWxtAQe{}?X-f0GPZ8Ubq=q zYmLuq>ge!MENEkC$Ii#lHSY+8i%MK)&$U}5tBJ|(yae;`x$2PAGaKl!hYZ*AZH-G?4{ zV9WNOKL7Houl0}QmCTwoDe%zWh!)UM7shq}c{=}Yd-};+a8@hQvy<_owrJ%2Bhisj z(mkSDicrlJ7qbX_JQjaUw&Q_Ke>+K?WM`8qaq+<1;o%V~kG|Fy`qSQCDnJcH4-FiA z=itBqACJ)dVE_KXp3oov)YtcR-=BJ5xgPpkDeF>@mzE9F^0Damz2=?}L958LZWn&k z>jUa#`Ew~}lpajU)u-NPczU`f?%OeOAA)^=;^ympC?+mOSz)3u67F@UKUMrhMf)kb zXs@}uTSQv)5k11nc+G=OwcjU?^qS)XXarC{@zFYfaBqAR4MVygnY5JlIQ5!CR?>aZ zf~N!Tbh=O`dIg^rPU$=J8Z2<@Q$5LN=h@hK*{;<67RI4&t5ElvUfLTl+anQsz#M9= zCo^rNuP+i{7}`QLq_*HKGM3N+8%*3A_omvKL0f0>-A-F+ovvwb_Epq$T0-KWAij{Q zBEcM>0hDB5v4E+XBwcYeU}_5!_GwzFDoi`Yq26S-^A@1eAyKtg!J@awD}abNYnHqM z_rYkQK*hg+sY+?7|7;}8sp;ZH1nQ!poEXWroolO3jzlq%Rx}`HOJa#nl2}U~9H#GFj73v$_oOAeaj(V-Nwe>al zrw*U)bpB~4((!URtoQHy6B6?WC(Lppa5V_$asGEf3j?T z4m9zcu@+O>+uw+b-{F+_8||ll4t$Di5YIqNUn2QQ2>pS)UL?uoGsgMnIRE?8&y)Wv zo1EJk2`R;|CiN*eB{4oSJTy2IK1{7m+|q`_d>$SdP5v+T-UU9Y>e?UQ`$m{d{gtX6Brkea>Ec?X}ll zd+qf=aRk40!_dv7#Zrakwd5IX9PkNn@rQxnnE~Sfl9L3_hdn$Z^l+W-Zu>7`6?T@d zgK>wFRQ?>9#=uX8eI?+le;u>sM`+dA5hBZ#N(Y;RXP&bxX}_g@^d!r4ATE5bIPIxC#@2B2%y z&WAnxJj~LFP6WNuK2s$46h(*RX?4g!jo4Bd$kQ#ZU(d>2=5`6jZdFyy?Me=U=6De~ zpB&OF_zYgp>oGXG!LVu&*EA`~?}H?KZ^4TfAqFG-cc{#%}68e%d@DNr83U_kUPa zls_pg?YiW2r@5{5%Pz7Vi^e$E02r%+I;TUJRCNW~Kd{%+fbm+c9a|ubQ#8^lwncev zG;D=oCwdwa)5)IR-T<=ih1(EV6}jqZP0Atr;9WsvQY*d<4>KbpEV2M#e<*;AdrVTG z=y5^R(_Rv#V&Hl-woG7x@+S}`t^p><>%yr%6x+kT+JCs^tFPKl#w73DP}%tTXNUf_ z=VcERoTE!h#=;2o{r>YqY=j#@xKT$*B&<*tnz&e`CWZEhjO5n}HJSupx+lx^|@U#NZn7 zTC7acd8`)7#|x;< zIT^-IZ-qh?ho0O1Jq@}__8#&<3PN~XTpW_rLnCMuKt&v=SP@|~BCH$2Q5!Ac zmT+@0E2I%{leQd-4jj5&kI;)5+k-Sa)UO`>PsbcZo=5=ZOpbF=Sq-q)!_K5|Bz8$Q z=8RGvTjf+|@hYSb=RhJ&%0G%;oQo)ht$2Bj<%zRq9WF<;czlf<-c2##kyZR`FNLZ? zA0pZ$oD4kShYxoK>`+fc2D%R82M`D>g-KPTjq=;^fS&;F?*i&;0XO2GG4uMj{lKLE z?e+QU{m87XiSGEkpRa#3s3kTnU;h#&t}Pe`=`KTb^qId^AkE~hYKnH;YWGz9?M(FU z-CHU)qS?<^ytG^S5rD45AU>cSdbq_6Fn-V@uB^gv-@+afpi9q(QgjRZo5rPD+26p& zir@~?d$PT`;r;6B>h~L(+fVj7#-47jeP;dBwaurmQhuVTK`7`=J*)&^nXDb%IIqWd zRrs|U#|OCjOcN5?@mY`SYUKqvPCeTLDf!FR)~`EZ>J485Ohe!xC%R9a0z*J5Ob`^7 zR($CgMEV1JL4<~0L?(h^Bvbpnr%(0voJM35Wet{Bf~rvn+@nb9_%8am0DW8t%-#sh z-UQ4>+91hk;uXE)UwZZM;q}Y2)w|TYjLToxwsEcJabQ$vcnulROVpCE6@PA6zG>UG z$CtAacIfAj-IKDT=r5rNCO|8It5+cvyo%3_nu_QJobA+j;;X!fH=S}>k2RUYd|!TC z|Bo)8iQUBob{0G$o~Q8d@SDIpzRNTDHMq9p7gc+B6s{=sYzDL&$vBVV$Yv!*#zcp? zC2v>92`@rc(sEK<8rOBUzq_Nov-{ZB$Gdy`PWB-sOy{xowr{#m_TrZAj!qZ=o1@rJ?r}5=(eo;z@eCWFCpSTo!Co6HW)fiS>Qy zD?zP^pw`gLNM0eB7R5S*X2-lJWx$966Jx{fhK6Rdxw%2f=Mwm0CryZa2CEPdla%H~ zDG9M`Q(QbH2?hkM5UprRY|*C27S>7=V|S+?I{}D_1tWqLFxm$P(6-Cd=y)djUpM#%4F#?oPQ>zV)9H?ctNIZ=CdCUep z`Us}OBlz3^^N%~g2w=S8$R;Ixtiz9x-zheK2j=7s7K!sHMya+>Vz^XmFjBz&cQoA> zf}!L5FTqLmFXw#^KC2zulo!VM+S+{Rw5~3M*bGAsq~JMHVTMTxG_EuThQ49+#F-rq z><3eLJ4OS@7d(eqI94Nm*9+Qcp_rIA0i%f+EwR8Tyfql>nyTIh8xfECE4=?I>`lQu z29NLaq0ft4ZmYjj8gpkcY*4IM=4L8Lr9}rH&#wk)r?NeKA<|90hLKK@?!IKb;3SBt z9?{$Nar=8acEA~>^^-0H>-KnlfLP57)t|wS|0XpaM0W>T05MvHA=?`etMBCIqCI(N4@GK94YtR)bx&hsFtL7Q8xfE<0j^HS(HUtoGkBtr-Ti3#Mp-W-a{|`L93EsLNdZ!V zvv|vH1v(JQHN2E%Ad#pG>F07W%~R%VyMi1%@OB8U-we3hfV+pzt+0$yU|o|$1{?IJ z^W1opj7LfG#ig|{7oK1VUp0$W>TPh;DKD)Bx(-QN?K5jZ-q zR58(lTU&HbW1Fz##-cm)NeRTU`qoz9k4+nBA6PZ8aNww57w&@(UYzE>t3zP}6tqJ) zxVi@vJl>wowsW$;iH`R01FhM}%MR{B`Jg~GLN=D#8(d;jb8{0~tu2bRaY2^3V}`WI z?7*{fil)NtWG8%E=LJ>;+J`h~YG7ZWRecIUOtRGZGK`8Z-yXzWLvS)^6dofI*YZJW zwUQl%#S!W+eI>3bA=CwIy$rFAE_l~=et=PAS=tu%LZB03Vz8ta8xI{kj2_gUG#&8Y z0{Dloz3VXrX)1MOJ*#Ig3vdr}vX{f7Z^p^Z(Z?WJ98kzlYmxeX~0b6UkpZo)ap7d zkX2yP5ZFn!C%SJIe(QKYo2E#YWOhu+%Y)t9a;XUOLs5>#xt0mo-N`0427jxW>g^HF zH;(%Gy=`B$G=BQ=VF1mmK6RTvt*Y7#?eEJ~fO!*p3(_AQZ)xLqJ9H>?Y==^6ZA6@1 zgzIcv+?Y`@qsGKxh9}MVV-angJv}|0ZEat+w6uKL7L@C0JwkCzD2w=&;3yQUnN2Q$ zA>JNLvmZj3CAo4Zt28|F-K_83I$4QqYTEbSQGa4?UgvSYbtJ|XTXRdG7L)$wz*gwb zw+7w{)C6kP>x|E?fA+~WEXx(?bV?lV=FQFUY@&$i$`3K0Z_tF+_b@+yN+{AMw-#LO z!92YUDZ}BH)i)r7McD&=0x5A6IE+bxBAlVzv+&hJ^hW%_-J2YXVb7pZ4{_3=s}%BB zaFSz*Gf*kRl1pQy0gpgH^IhdMJrM)+?a-|F_O#wo1QdCTmV)kK4 zWkD23pQ1}x7w7pE1Znqho={0QTr3n*b`5ypM*G!~21OQm#HdJ@Xo(CLJ>lum zq5)yets_Q{OIP%a379E{^)}&$&7o;?mdx^ouSki`DUv41Uz8^V$v=U`4aV4nNmnIY zi9Ds1@It7AIJHw-YHU*?$zJ)`Pyx(|G)y(MAHYrWv1yQ=3e>Sg5jeU7&{b!-F!8x8 z0}QrF1qG8+B8(m`%l}05ip1gokt2-AHK+=L6pqA~y6lC_LQGW|%v!DC7Bg2pdUSGH zA6yxvSJIqm>jNl@OQjrbZB!%pQ&JSOJ0^vIG;xfvz$9rf z2$P0M&p!Kp!pt1@3e=1-hKC-y|E}*(f=awQ_Rbs-5DwG$?(QBi%WsGR#m?g&th@!% zXWAf(z8l+E_o=@j7Hx}<}B;eQ1ok(dOc4UmD9MG5AUjB#)l=YajBLx41;XTPFN0ZCxLa0xSFnPE*-LBYN?q?S-;Flo46A{-9R*?xtx%Xuw{ ziHTv}_BsW>4dBqsrt1JlJFIo^37w+R#~O@=QY5_L}0@TY|vUS12C9Pgbg~T@@x5AgezZHua)Pg@^E zP=qyzAYFqqHE=Re3GurCYnlS%JD$$i;@B*!jxqh6K@hWM)9}GY%jls>op#bt^mO;0 z>}+pSMh`{g&~s&UeLZ|klarGp{GIg;4P7iMH8phv>u#WX&LMc{`8l2#4qS)!B-3|6lb7@_(vMvc&Uth_xe0Bl%En0rg??URWuK{Vq$a1isbPiMyf~K zItp@Ck_{=}DCt!xTXX^46;Fnbo7gfwZxI<6EW@#!J%M?nRxF+}bFq`T438}QMd_`{ z%E*Tw&P9^9a7d(^>MY#i_jf7(iSnl_*8CDDuNLO!AM}h?A zVZHTZ^uqTsUdE}{L2|r>g8ZepM(?gXg&^*}j^^es8e4mJKfC_vO|OFBYVo*qH3R)Z zLv0?`D0k}{Lh3KDK%0k$XJ|(jmQFLYBTISi4%LCajwV)*!+^~(KI6l|1dk^U;h)ad$EqM;T!n#cx~X%@n`sY98dG7@V)`Rti*;`8DGWM z;#h-Y9d!-{xu(n}rFKxE&19MrjKDg?_*jJ0x#%^mwqz_MF}|>R3(du3$!uT}e)$-` zV3^^J(;J26ks%=rmXui)eZOev_gbB71-^&A67N)==q3nDsrEzp&^vS}s_98{Ly(?> zjM{Oo1Zmvys3my(Dpjev&Zld zRyqrRKOKwTGI)GF4;7AnY{b=a!yKrAihq%h-*%ERM>}@P5M|uiP}A9!Y%*Ao$2FPF zO!UTF>k1^iLVrSQ3QE?Pha( zO3LICq&F=??fbN4TmwEUSea>I$>fxRf_oMc3>z`}=s=1EVc|Um%4-r-c1YftBAD~@ z1)JB42-l*?jQAH=6bAZz2%#agetkv?V5S-q5s=P&sjLzq~m72t|hYIW%FE1mMr1z^$I>@qLZPeL(-ocM2yn0%uc20Pv`zrmMC#-9o2Gd z&eL>2%7g%YbhCg~6Y0umKZ<;hLY@{^W6iv~=DmYp`3?04->Z4M@|A6`yt1vX?)`Ub z-ma?o+xvBORaLv*W{S(j)XiG7o`^^OeeUey>CjS6zkT-H1yeztw2$%_>6Gln#U;O( z`|ID$ol{a=ym;{gzlJbahg-$X!nXBg!A*eh)vtZwA3gP#$N*H5gj5<{@3wNRz{(M_ zWro!{W7b^dWtgQxeZKzXCDc{whL<(?X;m>6@Q30G(VEK5);R->F1*+}#ci0kb7x`U zI;;bC$vJEn&*JyGviZF{3v*FE_{T0o=Iq%UH||ro98LDV8zpBl>nMX@)2m?R;Wn5l zIfWP3W(AJl<`&k|;)v>LMs6+McnBGJqnQK6?u32^Xf6m8EDMm8W%-gw7IJp^Ftdxv(VIUBYBmOQLpCQZB^C zCXLRT20Kr%mo>Shrqsg1R0W0%?LCjVEs))!e7-1iSC<*z8KW$ZMNlUqhqBSw?u(6u zi>BCE*X`4-Q2ys(4g<_A(3n~{BjrB{b7*ZH8U>^ck=Z;aPX@n8X7j+So+>QdxpV%K z95o**gJ)eifoB6XYChzzd_(QVjk9M@#z^oO)4@Z<@|m8)~YZvn$Pf??i8Vc177V0YpoFjy338w!ILvHOO> zkgg!h`XDzT9YY8Yq_aH_5C4rY7#A#V?6M^-g8taU9)e_`@W+=1XOZzkC_e_2AA$rE z<2)FmIoV+_{ljb$WS6Jde3xmE`t!67@>A!y*-j_ItKr3V60;I&8TAr`t@Ej0kXOZN zoyKEy>YqIs54OeHSIR;k!k?LR`fv2&Tcjdd8DWbTq7#@-|5Aq{Ca2k@mHK@0zZY`S zZ#x{ihKVA@(28A*i;Itg?nZ3A&Fxm)*|c=$`!k)~=s~0^h{*UmI@!q`Jn2~{dx~$y zQnP^P?{)_EFo6sV>d8i%cOS-&h!0ZchAO$woa!lq*_olG#nQXKJE&y})gBFAM{zHx zcD_B{$vO>ft!-@&KdkV~{aXDBYFAJ@^xgpA-X>?DneR)4Ya`#CkJ@Fn8MX7n-MLh? zCT|G>s#nTHb6;$%!qRZoYiVh5AxZ82^J*V~+DD>xTHT6ms2KH&@!L^5va-TsjZdm| z27Kq*p16-{Z@QOizY7y{72lQT>z-uRIaA5W4JBNd3lc)zKAEh41+XX0O zb{C*P^gZ{S&Vicrm}bJZzeSpvyP z0SoIB8kRAQ62)+?RMBy!nE&KHs08%Uad_ znEzW@zRv$V!vugKERf_gd5AE;3wK~+j}>wO?>mMwMNOEyxO6bcABC8pkT+CL&^V<> z6Baaf>Cq(ye~h`S$D(sg#JPglFj8lmA>qMJ!h@tUPi2S%@qc*GC8^P%2ik8Wjem$6 z2!(}@y9EqEMb;z}j7)rb%ot+&fK%k`&|u7_!HD^k42x58FkSMT6k5l~81sB$l$%FQ z^pu%}+Lk_{uca2uSsO>8T4~>QuvUmZZUI9cwGtQ5R|p>S;P^(ZH0p^TWD`H|ARD$& zlNJTrV<9T723J()!7exNI*2+0nF*GFB_T5~g!`nSr3t9h2Fj$*H^t-UOI;;#D6zRvnx6Qe!B9kcH_U`60yY!YCkOw(5Z zMtda9e^5Qb-V-KcIEOU^&$)}%bn6W{nK^!c4uKBSf_0XiJ9oZsEUB(GMi;t-d2C(O z2w23^U8Gy0=io_BR&Grw>r3cN$SsHDI@_NSHmlFB?0_SuV?|n#~)j(JTiN z2egYf-!=ahrm%{jYJgG+Hemx~r8s|9dXy^{=HJnvHKMbgHt?{py17BFGn+&6n;bq?F( zgfd-l+_qpk6qGw)r2V)HBDaF}P%k4oj?0v;>0q;tu6U*zrN(dH7&Oka{>!4`k?jD z7JWgj$0hp!nAPIx4aV*pTJqA^{V$gKcJ%S>VY}q>hP7Z=DLZJFWm z`w=k+6e78p*<+|}Xl<&kMu1H(Lqw)vKajr*Q3Q<&+TDsqt)|xE_#KYo_8p3G0jtei zkXeh3>wN5B!7*Mvpq9Y+qZ|a1E=UVD(8q=ai1Jfe4TA|`aV2&~mSdk-mJlu*p~p>D z#M^*Pyo7JONb2TYVydohK2TTF(5jdy)>N;<+6-8zy=K4?;b5@ghVHbxqLw}GS_-=^ zj@`d(G`Py0LyH=9p0e~@c`IPo%hy&=d7f}L1>V6@fc%`CtOK+DQkixThCbcsz?k3G zxedwZ^%`j7(7~$RIqFQX^nkLInZ%}xA3F_u^ zFba0XtNfgkKyoaB)n9j#@&p~1;@WMT6lHE`Zf-v2&u4c_6NfcHtlKky!D!?>Z|9EV!R zq1I&78rp|eY@co2y7e>6~9*aQb>Sq_G5-vXNoY?GW5W*&uDD4L|g z#$gj)&JFHA-p|XA!Z{77+9HnQpZzf#Ci}Cj;n5`HX@8u{3?=SY?@;d){>VymA)dU< zRwJQ3O0a#{>C3_y}k zxiYN(3^J(pwJJ{oc zDR^!!o=Yof2YPKS%O=l6{m6E)w{wXX(81nTU>p=Yb)8?#sm@WX;dbCl>v4LYvmp-# zAo||&eSE_ljd!rh;Kv0&i?aOu8`HF{JJN^Ifek$K8_&!5Tk*;+vTl7D)^YU12RPdx zRBnGkd0dQ5N`~+0v8JXop0x(w=|CVn8rCDQ?>qGse*LDc&8vtb;^MexbtIgWMq4|- z{&>%x_rC6liW!gJ+nXA@6>)S@QkZTRkPn>X11Hmf6N-{|4R9jvE-c)AS;UCV&2#7e z6YSU`xPZ0HAb!LxvoC=mwmnykCPvFDFTj#%!GglV{}iU48j2~T=#GxQ09{WFMVCl^ z)EB{-3j^U>;%(QA8M`izxAp5Cj(-(z6?b0(Z+Y-3yzNqWGa6f4{~Peu-EB1f6TD5B zV7GrOylHX<#W|#IEn+_XuBz|Nm+^o9^OR`L|PlzKpK&1u)1|Or;<6NIN?*s&tzk}wF+W|p3MxE9mfZ$QP)soP~ z@}Upak{Yb{uW343?wV)4+6g}A61_KS>m zDv+arff8WiVxMz7ye8!b5NA^aSbU0-u>fXqMQSTNJbDK*2j&i><9GCdA*C~=G$k*e zx&_@c53gp}DJgk=$AEdjJ78wrsKqYl3f+R2jwjH)_{)bSyTsP3tzgbc>VlE2Qj+#P zK8XM%x>m?(#hi~4W`uDt;|@DujnXn8(Jyv0qYr#~(28<758`<|czP>28u{BjLOjD; zENk@ui*tar3cR$&uGeOae?vb}Ui)96pQfOnW&ol!eEk2@jh>fF|8G2Nd_G4DVYevk z`9`O+y1G6+4VH3g>GgGkS0Y_0tF_>x(({e%y!&CSuY{vS8t!)D?)vktpv_TyV7r6z={)39UX z!|@cmkGbKzx6>3id2-x}Lmiu(>Ro&f6gzjBS8VDyMBm<*1Ic4QL+TvJ8W0yqY``h( z4dAr*;_Are&yV*rW6Zdu#8D>ZJ9bnFr(nBQWD}yOyXRf4g;ETgJ8pxNNt@oY!rG9dog6dSZAt48Mvzzf=AjI-%l;%OU$U*>)a! z{~0=W=rUFwNHh;voC7RU{P&xI#dYjAIgqXY$jU6g{vq25?Oq{!^$BtfILCi|SnuGxv+Urb>^LsDF|Rv|>M1A5o2X(Jeof}u(`ZnaQx8afS7<&bu2Us=EHwX*zZlo2#>XAI$J}NrW z%+8)x;Zx;3-F^JTNkvW$sA>R|sUps10B3Z0qW6qyG+V;Mj8UdA#Qrq|yr-e@floVL z9=zP6K*rwFh~?7H4Q7sa_`TaB!{MVtzIv+a8JU+DtG0jsj{}E}yw!B*gF~Nv&0-S) z2VXr>?K_E_M(`N53a8HUFsnTV4juXmhqThv>ZF^01K&!>mmi|lAL5hF4}{Kz-`t!; zL)0CD2IE5SO^gscVYqD-=>qjXy$;%kU=5_wx6i|_Q~(~rc3dedwNZ5~o^UmuK;9W- zwN7*GiX6-Y<-9_e%{JyhEngb&Dv#cx&cY7G0n8`M`7U}_4+Nf7R`9Z2*t`|$SsFBK zykMd7D4Pxyh7em*Wm%a}wQ0Vg6ZRR^3-){F^5 ze}#T$)p@L%SMyTlfS90SAsc+LR440_Qd4C)Raqe>v*ctKOU4;82po2?!-$osVT^R> zlTkNGCuH42I;U`pwyjM|GA_us3(%C419|7%BUxH%ENAB)wc}7H6AWu z%i&dCeGbB@AlXGs563!E)?l@0u$D|O-;n+aYb)| z>2UAyQ6rC?{6Z_Ix8(;*=S03(&?-R;0g z=1qtlfK3hqlsbeGn7C^n_=2`SbX*C+y%4O|u{}haBG?~dt*$E@r(p44w{s^P4aRm` zV|K(?ySv9`?c7<{H*QMf8d$YRYfywvBG>FFU0gcx!-=JfC+_$#YiIWub4$anyu4^6 zhDxYPi1uESm$z%zu7(zKOMZ))v@KUj_RZk8$qgkE=eOJeT~{=F(4r{Fe2* z^4O&B0AT7*Y*-DR5Nx;r^x6;D3qynig9t5*h(DYNg6;6`ZQFLYZEM?&_cL}!u4bK$ zZ7IHGeGVLMx2W^Q^;_utZWX_UI~?{ihqpQG+u9uVsrW8KD}ztI<(V8cgE`dV?+8&@ zKe+e76)T>p?+OIEY63M~f%<1wJow2092{ zybGGRb?h<2Z|gx}j3&gPP^5kcXZ_3ck5%&ZWbF&zeM-<6Zbn>vnvp<2y}go%`3dI0h(?bv zEGTfs>+6S@W>oz?tJUXs84!T4&u4XCJ>cu@=?(Of2f4*!F-fp&?!~Q43bUfwa4V;H z3qB-2Gh2Oq2pNp3NWQ|{^r&V8yhM9{6okz9I>}G)LHFdA`p>enK1BoHh{D0MRjyHj z$r3SQWK`1!pI70w~&d#W)kx>zV2HS0t$>K8OlqHY9!fJ7+ zh(?PwE;*X@okH}#F50a#MYHH=6Wxh$e_f~gl9S_bujn>etg+*%BG@VtGM6%r7^wgl zCpy?3O2dZKtP*m+`3Dq95Sb_}!^R?HP_!bLQ#F$5aj{6pOnEqIcY;0XX20^VU*X-! zK2fCHYUX?ki?Z(t)vP*O{T_Ua`HUi%aquEcQ&oxw8#};1GQ+_b$&c0|W`*H%FY|qI zSTR8K?c)K=>ZElgjW*fi(XPZO)HVvWDSCBSy}0litksMqg)>xhmWwt>htyA|P^4bk z!TWp&bc6Jzvz6rlBP_=6kAb6)WQvBeFbE5ln#SfMz5c$>4k?2GY4H3z;F%A2?g2da z0iIcaN7%wHaT|fG+C9wc* zAw@r&F}Z)wo;{rk+k(wxxQAv9t2> zcUelol&mRPlcIX-$`v7nHMnos`^Gk?pR4O?w{Cy&S^U-a^j|k=`>X%x>asz*ylK;> zEpPW6eH}k+DBr#(Y4YUBcHCH$vSg%WPP`%Q%GlT#yS4Q#7)fv3UDHn4H=A5h_4UV) zVK9f)Rlc#Orl!t^WTJ|gwmvNZJaTkGbMpx)+HvFeZ<*mNKtPC}{ciDtzbu^tufXT9 zZL*3j5$DZnZFSj?*TXnIHYqdhx=9(69ns;(;Qq_ZWjp|?8$bfMM_KPulOF*LqyZM` znQF?B?&{y_o#j`>$u|UyBE<-zgvSGje;q#35@9tO2l^1J%17Srg4xp6cETrEqO6F= z!2>=03X8}VAL{qY*~nVB%!SO1*sykkBR|T{Jj$#~O-NGb_ZzLpDc774`FcUp7~vL! zo!_Z?B;CdfT{UN>;FbGC_#K&_(*XGj3$m^{_Dg zxn~9csh|Xzi1{uav@ulI5@x1ZL7OaK*u~0Qo1K1=p+LIo93o$N37;#u`1*hXp&wiN zFmKSd{PBC1lCP62DSGKl+`L)Fy*rfEF64!dbMdw|7mK4Sm?^lp!{K7hL2t`M&?&(} z5^G5B;|ljXo10t9QMeft4haDoU(^PLFh&c`JuZ$&m|U8>Ox9u0%`LQZ52>7UVT_=e zGfHc4C8Msewl)iki|b=;Z5~`<=3)q0z#I--(fB0^FW14C6L3txc%ism5ms8u(u^5_ z$y1Uh9XK!vqPx=#lpE%X4#n-3(Rd#drYV7ZDb!|30%d<#jUMoyWC=DUD9$BpZ${vgTL8^KG zbF%DuZoert)%N)=WMAHOG%_{+!E8|W4yByd^jN+~v}OmE74u@mRNE18zO=~kePVn~xn!r5<%oI=+ z=o;!YVCyDe>t6xHkb`Fr#fh-? zljdxAmVrz{7|9`wsBwJr#5|3>{s|`X2os3Px`#WIXLNilV{vQj{U$sh@6w#=G!mDL6} zt09_zSPM!40;+g-aiq;ckEDP^ASx=(ufZaGGibF&SnvsY{w_63<_-rD6jAg~fDD$y zD2c#}!h#f#UL2pe*{JtEwwWG-D56ajXzz1TznvB&i2ndVXk=27%^hryQbs7oG{3@z z9PN}=3lL4Hx&QTmH` z?dR`3fSot~-sgB{4=7K1thB?VQWhtof8|jVp_~2`kM*jAmM>X?vBhUh@UiwA( zY)vBQctof*rpNpG=>Ks9!G(sgflX28DjrkD!Gj3SckrM-$Te=HcxJHmEE>TT8nHVZ z4aI09t5r86a_?p>?_^4W4m>qQZP0fG(6eYDqz0`uVyax*>}0Ei2Wub1huFM%Gnz^J zWoQjGf|PD;ZQxw=1%ZwhBqSs-?4SkPkb*Wi(IaEghLD^yqM)FrW?O0Lw#-bpJmU?i z9N}b7J80++6E%bW6<$`B4=>+B=7hn_vv@xM|7J3bv#rPu9H0$;co!+RhPq|29Vx+1 z3ASQzmyc_$5Sq0%u?LkWlyK@2YJ}D!xTeiNYJ=7dy8n5YL-#|Yyr&cq&jgx+U@BnR z>6TZBtw~9(OQp)bzDleF8(_7t0xqX4W$6mhZ#4RsuF&}RwRi^k8}2cZp+%hxN@-S(;2A;$@H;@TLLG}eSUM|s=?(>!ZW528tMYaGpckDEGJOqbo>)rI6+%{2Et zuE9JOFsA@!+EWe9eP&Y#=0S*uLwl|O!8!FDoaezf2%o->D=Dka!3E2wRp(%WA>k?w zo^gO@{3YSx6&1tqhiyTZI0G0w2NC9Z_=W&a)$j_&8NA&QN?+Unq=`Eh#Yh?`5Ep?q zWtAA!1*(M64N!%vju_;^k-7*?As_Pbfae;(LozjG;-_`Ndhfq9|PUSDqy*aPrOkcX7aVSCvml)SqXw#z7)%nJb!f;)+&fzkl9eSr8O zp{Z$4K7vOcQuhpwf>omA^GT4=v=OjML=3qo7mw1YUxzUXFs1@Vvd9U+7(@=#7CG=S zpR1-p6QrddQs>fe-BC27GK6O;!%Lx7DwXxfAJwdy2B{X<5?I_Yhw<7}gaLsG6C5<9 z+SshDSRl{Nx^2C^HhSTzoyB&<#&*DrwmU?1hZ29(J;3!^8D`ef8*L@Ht6Nm@*|ndEAB_K-vI;6A`oa^;z*v zyU^Yo2sBd^tZtjF8@e=YT!bKwzXZg51G@m?u$0cuinPwotrLesJfr%2hyhhOD;MYQ zV)0Hyi;rPQO=fIph@Ct+c2_oLh6cWCb~d9p2D1U8IS_zZuo+qW0_GmbbC1hL`0zwU z^&+K_QA$jdf?QZae;e3veL5?mcn!0Zpi*6H+qW%OgkB>%q-DSXO*{f_!;i6pZ>%r!TJ!M!_ zIEKBt48LHB5v!^3v_%`_NzE$B@XJ|6vwrzVKFWT?zGhy|plAJBj+iwig?;kQjx9Uh z`NWnoB^QfRuR2PdAwF{^?PrL|#KUX;(xA@bP-ly{sZ7&*W2z&_fpPhz@$DA2Co6 zb=QJ5b(m2YIDM+;l;Sd}12AYh;~P*+SXhPO7f~?}^q=YJ?l}!Llvy-bBJGjba`aqp zvqG^3%_bi>d%CCB+fN}D#g1GN*P`<_NUqQPv9uo3LW@Db4?!i|73;5+?I z=QpQ)18y@M6AgJ5aGQ>nGoYs2ms21^s}z*MK>63?bPhLs-n zd@3EnWC==U;MH*c9iiuI&?nIyHJ5~bSlL!8n|j%@L}ll&QWq@C1W@JW;5O5fn{~XT z174yt>MQyDJ{qKA4eJMoJfG?qUVa#6vZw~Ey9o1fC`lC1dD%NO%mn+&hLZGr|7Gt; zp*r^Jb@+mJ(4F$oRt$rxm+pwy?;tuLh@#KGgZf~w-9yGZFd9JFOSaoU)LDcYvaxGK z?GBdaLYJcN%AHUBTF>csXdHz`)}{JQD_fvFU87DW0T$G7e)$meTKQON@5WHC;g0i5 z55rEn_Vjs!uu}~ejND;&ETZQJc~z(f63@ryusb^Gjv#kvL&-F}ER6*?}UTDGV(sxXzJK%N^;&D`(uyxTpCwc^rc6R2!w5T_TG9oAZcgy}N_s{;hY_h=jqd-^5sAl%Rl zk^HUiAWYKpFQAG`)bIEhV!pn>WQo>>gz zWXPNbd5g7p*j54`ovZN)qR~ls$FJ?4K;yYx=i_7; z7QdCuJgkNkszH~TF`9_tFPN2v)zC}jgAy}wTbd6ppvYl&6yuH|LtUeu3$PTr!+^G5 zATy)1jG)aIa)!aVl6oOn2hsdx`9gtKR+pKn1q91Q=Yc`K^Iw;mFWFv=hR&Co37?60 z$)Wj!Ms5g0+8sls=8zP4`T12Fzx#FGMy;raOC>P}u*;7KZN&XEnR!^dHJMrGTN?Dl zjV_>%VRu|iYQB7rpD#62Z(LBvuyLwM%{p#@C4%XK8iti0E;ZA$FF&qn?i(UAYtT?F z^7*|z*mv4&8I+nq7l7*WHC#+)ZoO;`B!AJC1ogIF!$sRX$OZlp=!Vt+3 zlGXo7%#Z>vCj+-@XxmO5=4drF*NJUJTK`Sd_OuNdYRr^03p#vOz4^hP3t@dTmu zz2{tVMa7G6*0%=6IvmOd_o&#|uCBJPd-~2W18Z;p`s)*Y0rlE90X@)Ye14sN7# zS|rN|+O~u?4UEhp!`LR+7ch}v76)?b(G!4CdIFX$#WLIKT0)P?MgZMTmn_U;(XJ)3 zV8_K0H@%(fOS)f!AauX*?76#9m&<+WduhEzyg9_DWTBVsMnwQ0@%%1~Dt!J<9pcWp zJg^3FdGtRWsuYKAIY3dWC`KSz<7yU;x-kgr#r~(TzNnYts@CB(m(Muh;xl%TrP`2k z1w5UV@=V;^4HrX(U6=#hGt0%x79dEKOU!I-&7^Q9vW#~zD>gQZx?Qt@M5+<5)#|0{ z`fN6MvBU|=6C{lt@m0H!>oL&z z)+#codVK!;RTOCZO6=fVg``u9g;i{|%WXRGN!_leWEp#-Fpfe%XgG%&5JJgV2P^$w zP&PbeByz#S>WsGj@uvTwTdUaPg468GM(FG+VR!rP0({d;1cHVRux4ECcJUZAX$`AD z6qNHuym_Gp+ODjQ_tUylKp z=RqOUffvfNI39S>?58vniyR3VyFdG50X+O~Q{w_XbYS=NHuXN~kDqzZ_T?aU{jKV+ z`7%#7iY$~?!P0mY?N{8#J{w9d0 z7jA4WAv-Sq$Jkz-z~i_L2<}CgmyZdJI6lTXpFL@tmB!w!sHk}5?Z(c|k6wB#7?WCjsV}W`8eHlqCshO7L$|?rJxJKftK!7z zHlI7v6m>=H713yYM{DbsC(ghuJ}lhmhT{l8!iBHTyX@6Cb9=j)^`7{m0d{9!^!kl) zpv9#4D64-!Rp6!IXTr)?&v6bK{lqy8=v!LZ$%4#UG5VdX9n%y}h*6O`WN_8;9Gd(b z?xOsVnYiml+{M--EtopX^bXQleErw0Pv*dfF;AT(WyE~GbyXg6TBU2p1HwnW5Ox#9 z_3L}znTiefB<=W~`mnUCc}fYm-n_7~syB8tVB=T;Q7mu?)2II$sS|{|)1nj#3S|9J zXsh;7`|Xp&gK*zHdbG5(^!jfO*THuZgAHEd>+9={Bi&Nm=#Hl<6gcUG`Tb`Rxy^s= zy?-EMPo%pMYu-(ma~sChN32gfjrX@)pgTpcQrh6XBN8N8TI7s2BW>52nKroMMOl$}2d>$RW=$D04rcBAmfV+Bl zchh^@%FE05Ha7lsRSx;os1Hdi-)t~_N2$uN+egJlS&RnSb(f6B5#R%HBM?dn&c6Y~ zc>vw``%iw|-VQevg9(LTH`CLrR4Kv$(nUrNU{dQodh}@9$vz*&p>c&H3{4+1+s6P( z7}6OGZn%^fDc*dQ#u;elvw~xWsAq8I%kM?%SZ(4`Q`kZz_@!`Tpds2;LU=D-E;53O zNM|>6=E20X!lmb(trkj(Y%=AB=mO3CvO%7_kW z{+O>E4w_oK#rXJ!hHRMc6h+82?d|QSC963+GL|`9qSe~8G8b-Dbo|z|?a=Y@$c9{( z1BOw~%3&NSN7OYnestjA3CW0gLJU(_O%5zRde|#Mp7s4a+~v@Ye-n9WXJIO^2J>Px%In_K|jP5*!9>FWR0xW;Ft?fb%S8C znMLkKUTTS(Y+>S~DJH~k zhRI4!2ahJ1CtB9T6gE?rlORbUM@L9b`UQIS0rYGRdNu_;OEFo({iYG4Vx!})&CJY1 zkg6Au>8VaED3Bx#)vmBC!%he#mHqL>Ip{GY3zgBUFhD`zlYmh=aqz%LjZJmvMi>2D z&JLUEN=t7afBew4T%?xNx?Nb=WL3h8?wEd+zw6*m3_XO!x*gW>PO-ea7yvBMFW7T+Sk8RHMcvNSKNy|()i_Bu^KbM}JDHeUIxt5As`X|nw z%3crl`+J~)IY_^jj|pNZT(JP$Ny7xhld zy6V4tbzMdV@@=einJc^f~o2kgkkabED+>r3y(inR~!-ia<1{}W=4@qoN9wJncp>qv;TDrHiy}d}0g^CTK z^wSCJ_jlUUe}=>p_c~KjoR}h}CfPd=zXrP6yrZ(I$+I=NSAz4QWHRAHg7ZF?klsAX zk&rMtLK1vk?HwIRmkrxVWW+`&0vB8vz?vmDP~c+1QUPXCkD0Boy3zy_7daI(ILvyz z-Cs0+(Q(#g36C4=7&~U9-S4+5TeUt}i$3`?#?@5x$sOnun$ImOuo5~_x%G)vtCr@d ze*l%vmsYI!)*TgguA>m~+l7fMWq|U0?43Lr1NHipf4%Eqh$uqlxd%RQ>~HYI>BR9l zM*D6c`V+`Y?KtW%#a6QAVGDnVN;0S3z2Lzd$j$YvNjy+{AvRfE+o-9)$>hP_sJ8-k>(p~)Brq9C0}>fSD)4& zK76FP)#qzQuvSRM=TioVb%}udV2_PNIK?nb4AIg4vyk)$m{k%uMNn%~BgTr$l#nvgqWzIg*} z{tL#%U1-C1F)nBk%01;I4x}U5(iB_y;ZKenZ9i)__V;ytv44L7Sbija2*+VXqRbei zM&JI?fe+r<&3=F{y5Jt%4^vK#RB9c8w!o1>#j|^{-*q!CZ?y|FlWw#B@lwkF-*2XQrL|mTalUT zcl-vZrXx^BpzXh}>4kuZStg)48_Bxh2sf?A-@H zh0U;VzjxoFhkppqqQ^10zJ{^=8jhWilJzxG15mUI(X4)VzfX((XI%5@-UDBDmpIv` zu=b|?`zpaz%Mt4FVKA`8@YUfjWH{nGyLuAh6RsK)cLkPGqhrb2I}qKWj2gq}*wHc3&8`t= zy1I@vH^g}R{Gt#M7k5Qm+|nlz427vdZ-v>QrJsYACWDqpyYOAmlC8G(;1Sf)dbFjS%bW-_64+L!W^k6u(HTADshl94uyFk&m(pD<|at zUV7nk^Nin;yh(Fk4U%m>D$JexaGvJ3zY!{g)y7}VFn9dNA5k0$ju8y@Pgk`uJ^(je zDPmM~bhv0VS{&)=H;lCkfm2XW`2B4tlEjQ+zt3nu^J7M;NLT4S+1$st*(b%PCRwdX zh`H$RcX7MT%}>I&=bMvm9(R?CpYbl&_|^-+`>VkFeZYGT@SY32$JBU_(*pc2uWe~= z-g0KqqD9Hxn&!%voa!TR?AuVev+K_!OVLvDNAL%HaOA#2e(=N7Ej!z;`*HEih|1}kv5*^{9nbvm7;u$f=0Ej_jyCrz4UZrbxiA;^S| z-y=lDY_P4V>UXhLjsyS5fWqkBaD_(ejsr*iW8&ir_(X2OYa)0xmf<{|PgLG^yRuXP z_TR+xdwNaZGjgOA5tzEZn?%dee+FCl#$<1-%?|a|RyN0)ZxOeX-1J@N=H|GZPW6q1%5>XI) z=IE|nP5y{v_Cv7Tx%5(`T{CCy!c(!jkZqtj(cZ@?#<9_24CF(?}FsA7+9*Imm(FfTUQKiESR&KBY6{gU}?U_s;30k zB)??`U2H-=9;I_-?R|Mg4+|%-dXr zak;4i_vD~WG?o7KJDrk*e?<itDMhhT&O6|~9*2@@E_uTM+MCN^$NON;Mr{>LAwkLlL}6AC6@ z8EASl7c<)(fj0tgh$~iX#_sh5$YKGPm~`}**KIb28@x;1-Xq|TG#8W@S8Yz3gv?>N zP!4MP>9B`xI(5`+KKjuooo;Tw#!VX$vk!Q3v6!qvgxZ%)3nq{Cw?GRU+MTK}+?kbh zw7Qx!|5e87>i11q(;v>ovR~W0GNI(rleWa4O@*|xi&yh%;<^an=j73{lh43diWgeL zqui!}fDsE%`;UwCeE>|xuRvgqXYmjyo76IB4fN@{Nd3LBx*H)hy#a;y^t*Eco&mo) z*CS5Y{=)L0-r4rVTW8`kGvoW-hH8HMt_cXy%u`eIF#){@QHzdE$oe^Jvcoa?o*&@b zcePN^pCS+sln_WsoCsX3BQJOv8izLpqtEXigQ3m?ot**iu`k-bI?-c<6PP^)i|tXC z0iR3qo%No@lr5OtLZTh{iUO{cyx+eLoLyWkru(_%8Us}m7koY;V$9gY#4DrIr9*X) zubP{`JboHf9#9oQ9Y6%Q>aFcACb{0!IoW*B$0MMREYQdIK_5w=4@*J8nl)>dZ+#D1 zsUHQMZ*InT{H;F!2DWKOn|c#E_}7@19x|3ax8vh0rZH!r7r99A4-a&``_;w4>)!{j ze~D{h-D|CGVdTt_Zr)l+zT8t;wRSuNMzLBz2&7`ZNIRx+L{H~f&T~n~)DFhigL7nk?!Rz;iToVl~WfR@PsH9PDHW9AA2=}J#Kp7j} z!hn72A{Ro#E)cUe3k#2~59uWM+Fi=#Fy!d$?l|1q(b?^a=|2AH(XM0N%>4h6_C5en zmD&IBbMM^w!!W=ABO;D8A{i+e8MSOn?T7=SQjxJO85wKjx-PYTTiSJ7vzfVrnrmi8 zMP`OB8EfQ{k&(+985y}`RIHJaPKbm!;)o-RFwA{F=ib3TecOG1zxSPonYnYBd*|Ng zInQ~{pU*kR>r+K&PI57(WIY=mMA93hT$obhWHBNKTQEP7#XGq$6wR|ZG!`tG$x|-d z;4N`*gisz8P8TQcP)4Z~qx3jNDJL|G$pVzPabrdXJ!@;5j&z^af>d<<-A9^gi;CvN zcJD!L8l`yglbW}?V{`9^fC?_ic|5Z*CT8d_8&zf&9_B=iRd#;RDGZ%w=HG^VNW#O3 ztmnm0s2BHqw>TaNJ}Jp(cXDzps=@YP()GY45}TYwCEukR`344kun4 z{2d+s0AlOZf#CqJk{r=0KH&QfT`}e_ny3-qkjIO=2FBmpFkY1yFFR({y%?{u;KAS_ zv@g(3p(3;%p>5v;Td64wR!W9EHX8H<56gP+BFr|Kg$C>8=9Y|dwPL1NwDEckvh}T2 z+!;6qP0}MN!V+nv`UBW5{cqg|Y0)Sd@t@xWlSA#^;8w8i&#+%Md*Tzd=df=eGzd(Z z0ETXMOm1nR75`^Hz2#@qQ?1r`_lNuUH-FSJb#Qbvc=&M8FzqHbS7zyNu)XqvEnlp( zLR}#U6^*By+)=o00L6>>fk{}S$0}H zSOd3FZlw^74Xjzc0P2G=|2~8z+R)Z<0p6-KE`U!q&ceE&3!sILn}>b$RN6eK^7$}I zD|rs-^>HMwwA=03*s#mH?Fcf*4$`I{msW&>t3HP(J+KIq(`y-5QJRg(sJOYnvbaEC zj7e!M3rZ_?xgoq{IMnW46{QQXuFs^b4)m>T+|L+mdyXqb1*m0GhFC%cqeCY3QS=Nx zp$YJ=keNMz9!iy4GA61K-m*s7F4!smw-XHY15BP)c~r{{CEXu_CGP9Y%Bt{u;TF_dx$H>24bPhT+*pI$|FV{jeKxA;c(Y0C)M_G)-hpA(=}-?3c#V-*dbK9ABg~wHFU29` zy%RU4@Mq-fku~N5%y+T|&%k`=I~p5jL)}e=u4X}9j@co1Zb@WK)8X;+=FQu*X_KSr zct;0fsDr!Yb{dY0eCZ+8%gxQrbLcR%$=D-8^;?WdIp|PlX1d*(NMRLN8d%EHG~-Y1 zRaV@`LQ`plyu$81>EtoV=Fs<0$#-m?J%)PAus?81m(EA!-b`6TZDDz2%77^32R&2@ zSWthkBy)LrcXNzSqlT$LEAUhpkgKU(u~MTOAapf499#x8kLKnvQf48tZEpU-3jH6y zGe+8&65?EWDqA=Uy~{>xh{9 zZ>r$|5ZCvh!CsF;=NTY^lg_~dxl~%F@q7=W>-8YtE76)tMZ`svcZt*(?{bi)g(QYV zKSrI&j4h&;B-+5P@XL3JZ_5%o`E)f{E1+wmDR=@}ssRtOvhFKAx;gmne0+BjzDYPT zs@AMsvc6)uuXzW!3`~d8a(?wYos@~ns_7h^laqbAnT(AV-pAHDVBvB_#7r5^PTY}*_hrf5b*s)v@ zeFy5cZvwHvoUnJxSbDYc34T8m=M^hTvQp!GkUw$kq|~3mTw}M3uVBq&8b@@1X?5C& z7$l2CW<|j?*_2SeS_qCZ-%vT<-yd9yZggn_C<@n)+38dddi!vq@>i7EM!k@SUZ89Z z(?Y$VS-)g?MYGQ*->jzjbau|1QKcLB&UfIAk-J7^-$A{QoQwj;GMFyH-eksw!T!|T z*4BQ?1^tlobXw65CGqj{?dnUg^a~TH6Hqg~Dby{LUPtbQmX>nDI8a+>D9*%J;hJ8y zF#qNlmt(wacV7Xc**{yqfl&(%9d%dhp6AdDynwix=w8o>lRlM>nS#F4XNLS2Bu-ox z0m8&t@E9FNo^}_9LNq`;SSA?tPAxnT(Z~>>a=ju`&V2NP4gEkFajy;aLzc7{eY0BC zP#yA|Yg&W%7u`2SM>yc(Fi-0nRaLLP^fKG-u=B}&zXRvbQUt}wH+rW76Vle!=7u-% zj3nxc1@p=biv_n1*Wa)2%)j&A6)7ne+mo}Ena^BY0cc^1Bci3nU=WZI2VI$-GQp{X zg)B4KYybo1R<4|dHKLU>ndEmJd`!6o_m`FeQ9$`b)~o+qoNrAwiLb!OG>GJb!C-Ii zN$;?3ywM0p`Y3Evf>vAykxg4JAQN_AROu0{+W=!YwRlH09^h};T5(-PG`guqX2&}m=CV)-{9(w0sGi`&a~tNiZ8tHirFFYLq# zTo|VTIBFWU;=#R?b{&KdY_U%D10;UTv9Wg{pj-=ar-d`6AmIwXA|sF387xt}6|RC+ z(yH*6S_o2SPGSMC&ygid$8W1L1MEmPw6Ym;OObN8s*STCKl(Xu_bjPE$`$#7f`_wV zU->A6^n6W~t?94JmhP69mU%##m8tgiYj7vDqVIuH{VMl+Gr&LUl0dzAxPt8e&xRQc z#&CYvKcdn&w8H_8xsM({>xhh;Xt7M5VASc5)oa)o2iGbjYI3PM<=lNdlvR8T2LCeE z<2ifoY=3`$K+E%Rl=CxEuR@8L(bGqd9y@w`z}wydjiMW#av1-gy`=A~VvOZt9P=@b z>BQ%{j(Z?9B*f1SIrsCgdZQ<7&Lem%%!u&!b-NFwBkIDC9#II?)Mv)}{<%499n9|3UV|ZOt!iKdm#ZFVtB)M=xm1s4$8rv?z1;@2 zBfv&ssD0j4me0db+&VDndUJ69&Zfp4@0?1@p93N$cdF8MvT~U(IQZCWQe@YGs-~tk z8AFVM*D^NV1D8@h*ONXv9ecn|LIjaca)F zJ35Z_4=c_BB-*_eiF!F`Q9v6=6Rt_QCV}%BMti^ccl*B&cXVoCT>yzPjlnx`At*YL z^cI@aHf21un3rVvy#lQZAn0M(ORcQd&hp4`S_qsn-q243)g@ahVJTPO*ynKUe9*#z z5G}+mUR?B>rN54@Zao)HLgo2y+8U7g|4Frd(Y^XZRoNhZ@-$HU1@}x9aKzdTSf?7z zY_)^QtZ=E}`fg50<3J31H+nrJc_1Q$W~6HUf2pd7`ma(@vv;D3_m*N60M@caR@GghKO8yd-fy1cx0l6i|k3cWj zQpUds*Y_aClO%v!F`j7{Pxby!JjgaaJG4-)9KSzm86IVe?C}Y5b$Gn_FF5&7 z(M7E%uT|ri@#_O22OR#z&^Wx#fGE&9*_~LXRYHUozKNn67-%C)R!e$~tBQUK-K{ym z22QlL_FoX5$PTRxv_8CSM-nrKgOAU!Ihc)7a4KqlC9?tCeGgF`+6__~Zpz8tqnvuS zTP7&W0ss8RkkCOm8qWbx$^j3tnO9{~R~?uSr7!*}L|41u-S>QqL^Fe;-Nbeax9}g$s#PLw9RT{w%#|s8MRsbDLl4{s09dr~Yr(MS?&hah>4=bS~ z!jJ%RNHFwv!2{FT>ClFuPF$}cT;tk+sZR&cVfb^<Px}g3RlU?1^YhuviA533A$WPd*DG?py?HeEq+a-icd{cSHgOC%5IuC=I%Ii* z@Tyrd1(RUHLUKL>mB{zDplnuv7$*j+$q|7clOKWMgZsZYK;;%6)lj1E_PsC6hM)Up zY7QaZ?Ka1Y$VnzctoF&EWP&AE7#_VZ9aG5&mXW>qQ4$zm1v~~AM9%3%L01l1hBQ-3 z6JMJQ0;Pv8EHc6nZjg&+j|h=*N!O&Lpc)bWbof%}Vn#*+=g$xNdJvd^0xtunkAF-4 zSlIprUVKfN2jRHWNc8^YS|#&BD})&VaVm)R-(<=f^V3 zGMx?S0INZxosvbhVSg%CP}r7LnVATefq6s&cYY6^n3Pal3q{UWI~(0u3-QgP%FFBZ z$w5kdTN{|eMh7c#XyH>Y!L!!EYQ>1|6)W1>R=Dwm-sl&fjTj6x-~*{~ThjD(I!Af?k6- zlsxR82}BXB*4%~-BrJQls*3F378qfdp%}ptDUPcfoE7W{cJTG}INuJ)kp6>EKHXn^ z`5mcCi5Fdrs(WJmOio}2QdtKuQWdMRXF-^xch=!x9io+6&X<+_zJxsw%Uc?UkKA_d zL#~-?CZ&k|3o#XG_zz{W{2l%Wiwr)a!Q$A&ZQ;_nI_$~7H+OUOTs;KMMp-9tgwjTt<)0&!xQH^iT9$2NhT zO=11*Irstc&9y?K{sPW3oeoYpK3;xPrQ7Z4Vr>e~GZb||{yC(c!?qg9?GubDrir{+ z9}{4SiNUzUM8XUVL-JFJJnY!uB_g~|FF3+{XUQCW;lhy55#c-5fyc2vAM9;NqpJ0I zm;p{NAL60JT6tXmhH`X~_M_M$e75 zkG?xvO)cn>TUL+-jiWuIe;=*Dce8?7qgA6-!Gd7!X#S`@m^fNDx?%KSuq3#1bp7c1 z;L_kyyEX6!hswQjrMvw9$AHVOZs^pd6p27iBd!JAO6@dP%B_Xz>=SN;H&a8lJ8Da? zx0HK{dmrjw3vAyN_)N{j$8$bzJ%Vv6V0}*J03-vH3^r~MTfo7-m<80DhJ8`~|BV#n zs4sH8+HtyeA`Al&M%s8U5yr4g7^nXa2&0}Xe1~QvU}+)Y4YpjO5x-0$8qmlDBSggi zO(W}mKqD&ht(1rP&Jm#uk6a)^>5>V>^S=lMmaKqd9ctiE)kH~&x5@y`+ya`RxH$`G zhT`UCNjF0i?m(i2EWWL3<7{L^^wL7YZK<1unI4e&5kQ3OQ5Rd{6sq&{t33|H?NCn1 zBF>gK2TMmCP|@*REhcO(T1m-ldJj!Qql4wUSiV!p?C8jBb^uubOa}Th8*}Ij$qrc? z?^-*>&M~{)^?alOHsK&2ui;rQA#fZ3=~IT5uGI=xV=)eB#Vis}$~i61i09=Qk&nB} zvoLTt#p*{gYId+GSS$Nuk(C>k@4JHAgC(S$1b5*k!99be!J=SIum+sLO!Td+p(sC~ zs?N)+7Jqox2(}~r{o(^3czyx6j*d%{0(=2q08V7%I=C8cBX8uZ`9|K#72#RW7jo^H zV|~$)2{}ha_pA^Xi7fx6Q(jR8%qG7W9f{h%Oh|wsr!OG^D;XaY*IeE=Dme@HkM)pZ z2^-Td$FM?5OxA5!z@cA4c3`F0kkLVk=zmF9AX#?oC`HGxD@U>V&{0JsU;4*hNN1oJ zo*%>V&yto0?GBPrXR!g^j-^!w$PUx71nB>-$6`qe9m_S#$09&pIqc%GH-ipt$Mur+ zm+XCn<(RORMV{;E_MSWElakY$b^;D_%Kc^QyPI$q;|mHVz+myg+lZ%nt+w_w(!l@x zcHmR)70GJH&Hd8?whctu%f4qv*=Z=S-@=$*#$5+jY!^&HmB6BHhUmM+Zsk@u#_Rc6 zcP&_un=xa?^(iy6^u0YNyb=VKIB|l(m;!rp)-5>y?7ZAG7`5Ul`~fgg$IBC9A~lfN zhLE-NoDcC|D6$=4L^e)fn;noa*T`ZcUd0Il8<#jKF(EECHfG#}NxHyDKvHW_aT}vP z9&!(g4UJxUq7WWElnSUc`)@?QOhLa;bpy(w&K6=#=5`;lVG&J7cGwQKjfYKoHtx6s z=C*WE-O$w3(Dp_MnWx#+b{>~JAgb0@*4EDt_6GaltWJh(wg?i#BD^O{F|RUKGH)8}hg!-hYLlTHLyC_*{2a&*1Q=AyPjo zikwd6%2H*5_eBa>z$M%iWl{E>?&|6~?YjVD z-vv<&N9+^DKgq9RQ~)J1)`l-^97}V2X*TOGJ1gb0vH}N(lUb3Fo25FK1xK)6(Zwf2JX6k2 z*P{Nx*+a3}<|}p6%dh&PpN6fLuex>YtMVKlJLlzm#R1LFg~(WX3DAoWdb#2V>XA$5 zmLtT=#!ju=rTRy&S3Wm9M#@9^=6}$;i}AZwuHee=5=Yab7sjrb#)bG8*X`p9VRz{T zsel{F?96!4cJf26tHx1wz7D*l+|DJV^U3JK?jSy<8+#!hIA%i_vj%GEIL;6K;ZZtr zDvoqxgI}oF0}*3PpJcP3+*`BI2Mchuv>sju`mx}6WBtWM3WQ#|luJQ#e{OwD%;wGF zk638JluQbl&CQu&czVS@qUPq#jrvDLWo0gjY;c=CEZvBV(E|Y|1k^Rh=ZIEg%2}~dY`YZ?%+_|t;<%5iGjcfG;vs(EO~H4C0+a& zHya=GEMok4&?9a@7E$PJOkClA2}A8euprzHQ>`5^h$3v?3rvk2u9ITmVTyq6djk84 zp|zGs1uSK}03IjsK0pDuOPfBPEyB0xSjbdp_sX9Np++ip*!=(c-=9PC-3|kXD=Zik z!m$Vs36$z+9R8Ey)^F%xQ^?eDX)3SrQcaoAv$mWgv|1q4_iA7XR9yp4Cem5+x` zXfrl+ch@qbP2}=RsdUU*b+ImQX<-^JKr6&_0OJf@5An7! zel@m&uuKuW-5P8X*&z{kjTIt*=OCW)JtX6pAKn8qSviY<8^JAb2&XlPTnfx`|+XU{L7g zhxK$50iR$9Km#D>BRu&BBdlA}GF;KvqpvwCORe$|8s)uIBRL5ayfi~s7LH!9rTC@CYcse=|vR^?>cqEm5x;{GuQM^v#r5jnb za~7fz+xVg|P8{zabVGDKdb;<}r-xg0E0@-{w6vd1En1ZgU#1c<+{QLU6<>F@{a{Pu z(v`ZVFTOtq=4V$+sIjOPoT{ttC@B#+^~V%eB}ZyVCHz4{whabCx7@0@`D#&UYWjMJ z6C>BJ{cER?QuN2N6~DbbBQroAxD~O2apv8>EnSwE5a_C}*!bKVpA9g~ExTRQ)V%B2 z)fh;JfQcS1I4N5I7Acj{py5%A(&zOBfIHH_WQQ2ifFve+&yS1-f)~yQJg3k0bP)n7 zew-TV{Z%?)c;FmD2*2(c5FDChOO!MOL@?~o=e_5KktacoqVo=(pvZ!xhDF|~0`kx= zqEx-WiM#*^V+zkfisWH2WB{~iFPuIBaFmbf<00q8W6Ch#&3(h18kdXC5yA~&xL)Kg zkgpeE>G&|{8^ZPSf_%Lf0-PdMkI4_0_Pc^Q0t$wcl zvo9{pU1gPu!((D%;&j}(Uf;KGZ=DT$E3CpO*TS|sbgf6+hy)KDUN>8s5Nv}WS{3#a zu+d;oXY0fjd8|rq?_r7F2zVvKB2_H6<%l)Pf#>8cTD;NTW;@mK1hOc!Vy|CZ0CSVl z-h@5tV5dgZ)i|Slq5_^Z%5M6QF7#CSyPQhwwzJ1*j8&j6W zXr*l8{%i!Iuna{k#&D5ET)f7~UW0!H7BZEd9ov>A{Q)#xcFnq-9X@^9L#rNN^*{>K zCuTpm^uak6y}xVkUw8aU+KT2}5+1ei=V%Ht_8 z2Pfmp@#eazx9`Ns^Fe>#(SPlW3mg2t%iY`W=^X?bC#W~*S`M^)`o(9T9~&4jp(;hB zLGPpJ^Ps6;R0RRg0C*PX4LYKso%Nq$K~#GUqHHXzalzGC9Gxs!PHW_p!GJ$#w2TWk zL>Tx$5b+=a3k;t9{@adk&)_E2v6B)eMLQ#VDWI^sTRty-`>7{eS7ozD#)a(9Ef~yyG`-Ey<)h#H{SFgKB?$` zqNdblE6p^QVG-fXX2l$6&ddVOD}#(%R0RIP6{OQrQNyLHlOw^IgC{)ATKX zHJD-=*Pfae{@Q%Y9RO6&jBms8l8V*yqtK_10g8DA6-;zHYWBbpha(*Pw@uGjrMq?2 zIlq5611Fs!#?;%L&(yuX^TYGz>#suyPit*0%#DA9YyFSX9|6(?4*D_a$#GS?YCE=U z+flc_V<0g%zW1BXZ$7N8b>%2NB!IbRK;$gdJil}2_K!Z7eJQ-1s?bc!&xsl2r_KmE zG-YLlSvP2_bHFzW!P*GTQYihF3!serZ$qD!guTd$(#$tx6_%AbbgCIwtAv^N{&Kn# zf+ue)ek6;D%mu^2|Kwkd7^3&cPdVq)~(MksQ@(*S zX9sl?laeM|bY=^pQ7qG*b39yB0@m$*hX91}B)H==oQjeI83 zZ^@#74 zAA*))m9e)g4RJ8Dg-1g4S=~J8bCtU*R9OW1#C@ zHo*1?6>O32(Y;*vz7GN#ylT=s2UMx$~g`@qemlBq9y`#S6Fm# z@0KlFzL@z~7U50U%j(CjZ>wyEMvF)gVVU)o!HO(py;Rxv^<4``otO*e)Pj&Dd11n$ zx0$Ww3e6%b%7+EKi44P`Sm+_xx+`$j227fEm<`wAjK3_wV-9w2!@9diUUxr5$)-^EyI$yLiy1T=B({L-FJE0=zOkZWqljq^guw;` z#NnogSF2Fb$q$s`#XBQAFx?s1fq&%A=3ahhV9?v87%Bco?+hb;@y`BY7`wAy|JmR! zutEo*8$)ND1a+Pi1UVU)3|UwR z`)!jJ2KNW|BckvR>W`;Q!)az?*cJhpFip2(=gTitY}&RJ*?{KBfkOtYG1G9b9>=6B zM==oS9~cqhr>FXdg8FeNDn>4D55(K&ba|@Xb;g*qyizE5#--lY&kwgBKH>4CJ-*`c zK7yWiU`t4@)TRd@Nzm;tY``zs8Z?xWd_pLDlD7Od(@G9G+{*7(Yxf7?wsKUb1 z6^{X?T(+XLu<(u>Oitc3^G;D?-1ySh+LK_Fw@QCa$(3vDY_(|}eQk+l`y>wv` z@+VQ;-4f(aav?q$9D6xF(5@%AUB{f<(ORqYXYm)rb|KGH!|CZt5-fE4yC7IC38jf_V#?= z2Xs>3_dP&WpB;Ac!viP9mz3F(hJLvo{c;uhWjy+YybJ2>x80VrXr+y`1ul_GWbg2c zwA;lj&;zpa@L=g#;O_V!D#o(B6qE#59`;u)buFhl+{M>eW0mF58W0e^9+_QJ@-rAR+-tf#Tm?3toV&Aa8OwNz}xW#f+dVa~1hNZtQS-RwojEvdy^8$eA z1auQpQqAF*B~_RuU2xQNIU+Ua2vv#!kq^i*2q&=KV!@tCQ{a3`5ljBtWRZ=w7i80g z8$LT(!DJ_J6>g+@poljE;=MR1{Aiq3tbvUY^$AsuiTD|0zVlxQGKojjg-YN9=LZMR zc~AGB_R`nnspBLT#+1+l-lf-3z6doe7!c@@l#HzQW6}_LD8_sgR`RbHAPuG1A!n1@ zMt;cOA8%1C&=c`(suDunSfj?k@+uxSD`W`-BobjRY^if>w;GPLjyjV%#Z#A(;+YR2 zhRm^Xpci`28~ZFiBf=@jV%9tpj@-k+0S)l|cn{?>_ywrqK~Tj{@w;jGT@=G*te8;t z6yjuO_OL$}XJ=+sRed%UdpC!s-#T?s3c6{kNa|5VI3hVT7!i#iq#~1Mg=%1+dAm*e zpAcZ}PyE&o2Sg(M8-m9W|5}86Yrn#$We~$lRi0CyJd=a@J~Mb6q2%x#$A zbn6Q*IA3^S>rpWql`Pi=*8<*I2vlckYNe=4P0h$yk&%&}D(1quTr6WbGUeA|DH+x6 zQCO6wir0q}gLvqma_dZZ3gUYC8c2?_;To>PHKgMHaa&5svOTgDwvC=p^*xS>0VVD_XA{pNwlq-)L6wi17p7AiP%)01BjL_*J2k3QL za4;=Hggn$VSEWJ;vn+~aL_Lv&o*>Bxm1d#yVHJeWOKzO76u^5f)jS*Xh$1I8t7d96 z2-?}q_G<&RFmwpQE;+@B@CKj&16lCc6`?26)9dC)y5LGQfQFELr$~AN(7<}if>9*h z77W;##Ss?Bz(oSf7640G6S5|{)WttX?_{D=+~6`~b?(G3R}srb7e&M2KrI?3R00f8 z3y77x-!oVqJ~#lZ0G2#p1p?yQa6!W9hkRs4REYq+BP1i>2se(KXr4IE7^VWJ7ejrK z0IC@CD#)`GeWA`-yz0o2nFLMjLDo0Hyv1yWJ0Q)Bc!o0EgpD^CnzIm&UKOmOu@y|o zU<3}%VuB9XiAyJatwX+aB%lHLY5N(aZq*YhqGSjD$T9GPHR6)Syivqx%ca2$4px9Q zBBU?-9E!etGp_k2P%=qqG=AhejNJ3(DR)c#>wn*u{8QuSEy&Te1WaM69r?Vt^k3Kz zR=f=tw2iISFGzDUSfBhF$ctHEG+(nF`XxpAkw>~)zYP4OXg0>9g`Hv^O#22$w6Bda z0l(SiOHYZz0u+~$4sB!uoRYht)HcFyR0lr1Q0WH{+ufsb$C(u_xy!`PSQp5=K}^E4)DApe{)o+;Qv)I^oM$7-E(!$)$Z$Y8&; z7smN%5IG+SZVv7S*RODBg?MO&B?1uSI#H)%MuSIZF#=}25vVy*7WBr32BWdXSk=%_ zCC)=H6J^nqXu`8aHmXyFaoG_X!w$E%9~L**IiJs=_A{@~cd^#_bX-?T=(@-s8HxMK z4ORwMNH)yUdU$?IpcCE39~@@Z;4%lFZn0RIU1PDN$0sKfP`eU~XBo!127>=uu+jB= zfe}}@#i4g+WO&KK+S1a2DMstvgCE9OxUz_>noc`23{uJDP4>H zoF-i@86b)p9dYPO$RWVt4xEcgp7rac$y27J-7+Z=z=BkF`pUFi+)`MIjsl?KlT+@r zVQr)Kgsh#`I^xjN8;!;WpRe(~eYJnvvZb~4^A`7Uw|Gye&s*f4??m4>$>VSw3}^tV zmmiX*kqxt6P(B0KZ2D>2#Biz6?Q<#fNgQe=SS@@h=2|w zd24-x-gD5*@qipGhN9$4&@1<&R|q3O^E}Byz0w!lM|~nC@dq}qCB=ra4K0L{GY^55 zKZSL4I^;e*-sfOXI+UC|aOQi!ww6jyfa+E|64Qz1g!K5dNkE|GF8fE=T-*`pxgqK>t{u_jnyo|En&xfj2k6_JOERSnI0Gs99aFxhCyR7J*2meIIc^Cgb zKLI_oITGDOuSPd)x_H!7gL%AB)3^u4vg;dQ)%ZkQ1R5EmV2By$I|Ca<-%&da#TNaE z){jpfrDySta86Bg4NWg@Pc#Uc1f2%X*b%rhICU6`4Fm%W{RR_{*LzOfjh`j_mLYC}y&C)?$4z?%Rl6#2GonYc5= zMk4W!s9LeYW)s&q5S~P@9*?+2nfWH1Ya-4Sj&p_KTwyp@7@f<|*-=C16Oi1uu@O)$ zoKmo5ddMqXVV6%VpbkJ;nM+IOw%6C&#lJWJ=!Y2R!Uh&elgSmzqn3zYNx-jM&R-B5 zP*=CBHhEr_LeQdqx4XHiWuI%^uFl=a#FCO?(#JRN`fGKysLxML8lRAyIzArNnpjmX zRkhdw>j*7oVAZSA4iIvH2-LoPSw znl2^nR&Z+oj=u6jvSpr7$y`XxvsOq?gzWr(dOdnmR76yyMjalb|IzCqd{jiJ?=_at zINo#5&Z8{Ru|~ahtHlh$v~xxy0>U;xwCZLX#5G_}jJMm#fu~rnlW~@7LVX*95hNa| z20^v%J4z|mZ&spc!D;Sr=u3Lfef60?@5VS!iAWl%hL2xiWlyV?E-ePFY=p0*5QQ{0 zy;@OGcLd%Hs@^mYv!n*riO(TZcpx2o0)?d-D$53n2M7%Fk>Q$P3@wb(Iz7@RoB^GV z_V;#+AzC%#>XJiO7lo@M?Jy!S@us|aIqNpP{OT)PSvMqjZQMA)$#aGXml{bSdpy2u z@a?tiPoRuvL)2zho%{ORfJjR#SF(H#WSkE$BO&MDwUKS&epiC5w?T=QbCz;g;OOJX z5E7Td+6l%M(i+vc9yN8L++b_P^;p>f(Pc$SP(`bCDKmqpWlHGE$JRux48=-oK#ta8 zYIe*Jy}omJ)ECT{8R^y&7Hr(~8!bp-PncS)Ch|NnJ>3NVi^1;)D^eLO7Q87v{%iF3 zGW2*BMkNchLXke|)-zXG^VY+FV0dP4tDgiwKY#YXwpkF%|EOM@JM&b_``PfOD77+@ zrHA?VnG4bTltJ_n;iZX-i9f~t!LcYBv1d48g^GeK_Vhd@xYE4i~w54#F%Hy zOtnPu@!>h`1?<}NP0E8hH*nKRjb zTqo3djr~oCDw<$6*6zjNA=Dlw15yo%?PwAA8ArZsX!!J%=H{9jfD^OGs0AVII#-Tt z`%ny}ufjFORaf7RQ|2l>O!kKnH7o6xQYpa6Jw1H`a1;iEa3gjfKiP-4q~T{&sj2Z{ zE{$ap^s^VEW+d&Q>WcBci#M;v$q{~ zVc+)FgMChZU>KpkW$+=O3HIlz-}zc(@h~mK{`&5MyRS1}lbV)3&8X9#X>b2h0zn`g zfcJVet97||0D`|b&wQ1Ow{LXaMbi(KNrg_Y#kxh}k8&e(Ve2gfz5Y?Gj5V-+7Sx#H zBbmW}9u6NkpAcRx2w|sseNLuE@bt*wIRFdU=o$nI;t>V9c2&yK=;fB1Fde?YgFAqfCSe7`VRb| zvhD$#gb`G-sEYn9~?y7r1RNr%W&z{~m*eo49+!dnQ$TBi;Byr0Q8CM3kp!k$p zhon%xAT|j00PRchEZ>GR{|sj)I|Idvlf?x~F6_sv$gL#!#sis~t*ijbtEOWFD+5Cj zHC^wS5w))M{eC1Cy141m7Z*P;=hpl{&o`Z&*;qcSk(=@v6js=x&b=Oq0YNr3IMmzK zakRI$>uaAw=XHOD&9A@oVd?M2`c})<@ukIObKs5N0Y#l;fr~q5kL?s+8mAMqVMz2h zLTQiuBgoI~J41pw${|Q7hk!tev5j)OHURiO3=PB1(AI^a@|R>fdJXr#8~0B&jF#g5 z=i&aXQohjDwJ7$zEC@zvANST1OQHZ!agFud0$RGJs z#crUk(mn^Q&!DuRA2Z-S^$R=p4B+miG>6POmhpSca56T*Rrq2;$t@|<#)D_aU7M0& z@&!!eCr_Kq+PAOGNV%Wl7^>vED(zuz6W7L_;9Ak%<#r+ds|qKu%k3?OIz7m+T!wv0vt#=qm^4$N zGS!mqg`iq6e~SHRQm?e-rzaR3SnQlp<1_Q~Q?PVL8I99#nEsPO2is=@G+bEdBf16gRM z+&ER}*Xv`#d#=(`|@qWbLNZy7)xyeUEu1$87tb{Dl2Jo3T8J_jGE zS2?v@7$6x)3*v-=B06*y%jEG?5clj%TjSIT7dkja8=6uc4=%VgTT58ow?`%Zj& zCTQ~}&?c7?i4tktex$y5S{{!U_OxN?tyqL^t@#wMpVrubej$FFD?I_f-Ew&>mJ1fk zi6#`|k|jCa^Gzo#m+w%~4wN}BlNL_U$AA9T(@#JB*5|#^(b2ufKYaDoS3f*H0;(Pn zC#F39c#7M7A$rQ}*|VoaUvOW(E#Ajv$O#QI5Q ze;+_k|MIe)rphOCz)56=86cJbIX8;c@&1d>JareufGUSg%utb^vgt_IRv0X2=I<^ZHW z1m|Z;IZ~6xL4cKi$)H^vKc9V?CZT{il&80oz?YC(j%?eB>w&`Cp^3 zc4E?mxWr^jg2fycJ0WR8d}2~)8!A}*6+aRY8tRbTMY$E0VhpIhpbgYcRh?#W8s^bF zHm$SnVE27CENDZV1@@c{O>xY}TlTcsvH);F8WJv046qHKoNOY81vC+nJGK>Aca>{v zV)1P7#Y2#_HuL81wppb|RBN|S#?CE>5^P{w!>;mI0Lq8pPCsS~@vS8gebzx;Uws!` ztiQz$7(C(cvci@ngKl7}p|BC%MHfQrWiABmc%6J`8+4s>I4% znv#;Gd3j|eVll4fpR(hi74P-9t(T!dy^QxC?k5vnB{`W`H%a0wAW!)KhgDEo$G8_Q znhTzRA#G%0O0vZ`aH8Yr@pHra1XTDNI08cz8LLDAoJuD+u|$pnXQl->+5l`dJ30XQ zcK|PppDcC{rw4dAT8fc48(T+mdIPI$T=d98JBD#%x0y1+|invZ>~n* z&(%UrjZj$Fxv>)!dAe^8HlTUoqzFf^Q`5m1dOFxZ!ndxf_8*uA|ENVNmQpM$A=gHh z;@YsohhYgsV+ULGNO+HaJLBpT#Io$2(MV?L%JV4O;F$hjx z7>kk&LnvtIA=p%POMhs%ez1b#H+~ScoRn8YFZV{iMPXS zf=V1{(YP)w+H!k?9pz%VOd%t~&~Y)vaCL_vL!4OA*jRDA5v6z=Wv7|u#QU-XjjTdp zBDWsQjPgR|Q9d<|>-qPa-hbAagBdX&;E-5^59LY4JYWa2Q6PhoQpC#8L4bcnm^k1M46>2ao-+Zai-|L8eOrLOrTiP| z=-aE%w>EjG{j3?TRe(jP25U1r&sic03Q~McfWtKTQVIO;|UY>H&^l&Q&+eLTaOS z@?T91QF5P5$>T0jaz=c72Kx2?o`~V50O`Iuksm$Ne>TW#W8?CoR7^%$u*)V(pvv%UHH+O8`x_RrCS9jF@eb<{c z+qYnQ`;Nc;eeISlYkzzZZF(0q()Km%+gtzEyG`%zX>1B@@87#;-xWV{aS@fdGtuuI zKj?G2{%V+3u4ey+ng8FEMD-yaTe(1^TcvT7GzMUgaZ;od$=!UjINoYiyWg?f?eDnN z)+@H9sobtzKok96_w>Y;mNUlbm;=*|XMh*Fv~5BF0w}iy!1P1VRKLTZyX9v)`xo}n zenNsAzj8mc8cK?tenl@_>gtjQiwX)VKy<8GnjC}j;qaz7W6B0&_zD-jKRtEzl0tVl8lf1q*xx!y~_Jz%_zPfc= z)#ewG=x`-!m*N#&uF~<8Mj)^jb)!~NsTT^?Pt>>PAO>+~J-PLdqglt{pYbRS5 z`G_WABnUG%i$v=%tF^WRZvT#2)asP&Ps|CYeG<2GCtiijOm*iwAt<%#olbS8$hlmK zq3R*X<`d+WEj=WZzxCEz~U0Yw; zy8ZRncl@np`>QWQJsGj-5d{kBD)b` ze2_F^}zHy@dIQ$8x(xC=L|2Gi_%e&{-1*)BxFI7#uVRgM$K) zJutB|KVoTtFbw&p03w0ktSPXimM2kd?Q-*Ia%7s>i=*`@C0^^b^1iZ@fxB@b7<7oH+z5{6K+2 zvuobGT?Rv8C{MGn)Dr008cOlNJ@*wA?Q(``)PW%au&8xwY|v*KK?Ga4m0a2K`6NGp z39sTPxljvd2c*-7WFJ-BBTMgs-C_|p9g?z-9f8)PSVP_c7YRV`%t19fD`y3!9MSGV zN^fsUGiB8*mhBR$(gXZHmYaiPAl9LMfs|(J^%lK^i_juq5as~iku~EfvLZb_2P}1t znRUHOzSDiJt@Rs2bR=x)U{ik;GHi3p^Q@Syu%E!BSetUFXYY>PyZ5yp{`|muyLS=j za`VpJyWgsNYxl0V-r4=u?%liJmS1-xCJqmnupyh2W7bfm6Q{tzX27kFL$s03*WC?^ zRL>wYOoF;G*&L4~X`G68kj-M$zXsADdF;vOrUyk`Zfme}8yRXWDj6p+B%CdTu8@y_ zB#9Aacq-8$h_7cXU39U%cy7THK_Lj)b1CO1AI6{X?Yr^q+d&Pt;`DEnJ}F~r%?AEYS@C6e&b7T9FTFO;p5|Xpoq0x2~GXfu@t}Q zPD*?SdSO+uh3?9l8k-Gas2B);U#~fm54vKllSc*x+pX6{UN{0TPfPz***U+V5@r9C zH6$FTM$CZrmclvkhZFTEtqu`vCCo}c%Ik(ZvY8!oQ(=8it8Nk)>b>0y!=0jO{7!ce zHs{(}n0uU>)38*lLmoczDIK zf@y()j$L49Pm*&Y&cPy_oGuK%sn@8&vr`o`9ZL z87me#qW0chxWi>|G&SxRPQ#o`zq6*{%6l9FTEY5qTl{(lkuza0JlTlD_}=>I4PyIXgA!&9LDnhg_fSO{l? z`iptjh4sE0nt^TM}n(yermr?H_ zOS&nz9idxC*7;}5m~sB#cBE^krmF4w{!L0y9R&k;0`g7+OCgV7&Xpr5+GK&w1ZU7D zopo>zbf%9|{*aBDN4;DzEYNlADdYW_*~rE;FXLJO|K*33GXZtsnZIH#L;(soXvX)E ztr&K1C7s+x2TO&`W$GO$wC3&|)&c4nZ_CebX#W?i$US}zE{qxJNpbO0Qd8Y-8t>sB zjJL?@=fb>y56Y8LXqrX39!#uAdR&Ewv%lO0E?|?5*^3ot8KmhVL^3Eou6sa@@NlW0 zd9A+Q<+}a9(Ffc-+|!J6F==!9kpV!yvA&P`&mKG4{cUrP-y>nDdH^;X7S<0{e?QYWDWDM1 z2cxbOU7Ud-sZl_ZzZ4!BIWRa#$;^R&L?K}f;^qO9ABH{XGCIRAk?Rx>%87m1L-|O_ zs}#RWD3W0-sP7$6-+iFIxuCvTpgslwJmfO82xuJ92vj0{&5WMCN3+f2={|9AOD?9& zL+JGJ_|gP;R31Y8nJu4mBep^P(no+k?a09wmV!wu-o+xorF;;rcXLZnqvnqo1!^eT z$@xZ{;SKi1ITLVU6LJife60_ z8GZ%vqZ_IV;iWu4Y95CyWpw1iZdJrevF0d-J-lkD@}GNJDIggd*JrrY&s@L24WuDk z7nunfgFUb{5^nrAm{XTXY%b)MBJ@qEcrQf2N|+Ar4G|w1{*xh;D?~e2T8`hUg@UCp zs6*JUWKQ9JOsD&;%!y2%E9ZhYR>C%?FfM=>@M$&V(zmX&#CQ?ick=r_T6$4mIwndr z92`|g#f(p$K5beuuzX?BG2@acvvN!%2XFMydQMHL7)B{-9zpXicyU?KbzMt$%mk_f zh&*_B=wiB055t~$;Z*mr_D@?sZU46SR{nl)o5Ih24~lva z6lDWNQFc_a5L>0&C}jUkAFkB<-B(}O?;y+pi=Qr9bEhKV=KyhDHn^DQ{B^!Sd8CZ zzN#3)oSZ3D471y3*cL5Xv~c07*~|e^2u=0N8HtIv%Q%X$g(OQ}SQJb}j`Roy0Lvv0 zQyFDP4okoT)s4+&-Wf9zmeAGJd-7QK@e@4^wZc?Gy&@AJFFqB0fu*J)hvUVEhJ0TB z)1b=ZZj^)JhT+)K#lo9D$s$0!gnB@xZpFBeP563@%LK%ox$O)<*g?9o(>ki~(n@@M=%}#Wx zqB1k@q2MHHj+lq@yFYyI0AK=k20{KnPR;?a78i55RPphj*=NIbNOI(ZD*LCVl37@p zn>4lCPnxb;@FWFF*!d^#x+Aa_<6fMP51OAxjlM{>?i1 zo31Oqd3jDrLTZyvK_VlktK&@a)+{NPxgb0ucRlz4{kyzextT$dDgbLPbyH$EZ|7_iRM=n6CP<6gWY3i3ro1`?|hMvFeY};eE;~S5Mt*zeP zX0%$Z;V8VEqVDcqvLpZ&Q0Bm`*)6_+F#al&G;kiQX86oE2M<6VJpOgf!OY}`5k(!* zvde}zI$5BR%7m>alWs4|kqOZ*`N1hcL?kExYwVV1|0ft?iYr89VdUclY3aI8Dl1<+ zmo|C)GZ9V*pb!^(||i)Vblx4OFe^ElMN+PJYEtfCRuCFNQ=I{tm& z?V6Wg{|FQD>yA@G5+s&;pIoV{HW*;l30me86rzfnde!ZgxD$wNp=F|!nG|(al*wIF z;`5#T@^72wV$r-L%1noev=DNi6M)m_?fDR4Yyg-Rqwk&s%=}5r%o1?D`Jol1Kzi7) zX479k0MGLk6%{dqqg;Kq&o_8s6r5%?Wvu1a3BlpPGw0Xc44|9O+jCY53=f|5^qw8M zaDHImRNv|IL;XHIABk)S{NUN{e;@j+ph9}3WagPaD~)5y5167|CxjTDr0k*36?q@;wX zFdgqhNi-e$5TstNj*2trwJKcjYGRYYbpopPMu)GH>H1foxIcj6s51N2ptz}^xNz6T zx_Tm`#7}ze+G(OE;2ir%YPK_OBAod=dK%>y#2rb-2sLIC5IJC4 ze*ckR5J*kd)6;X7)5TtOb#ii2yiwet%ySgu0en=c%U+0HznkXy{t9IJU&3{D+1Nqp zfV>zl6t@3cHz{d0up`+L=6PgA&F4@64!-2ta|XVMtrXKmS@Dwb?>QkS{DV?Cw83K3 zbTxwylsWz%&b|g9sxoc+IcLuNFbpu@s3W3|N`{6;MutWkfj>B-~yY6$qpLXBxeZOxW zW&X}NbIx;~=YH;=>%MNSz!o|jN3-aLa1Fqn`0ii z1zj>=_=I>Z9FMn9nuy?^4in&g+~K2zlkeX zu6$@hLV`68CuB+*F=l+`gfRp4p%ZX&ATl?w)7$vPyYKEgqLK6~S1#=AjGZ&*cesvO z$SatPewhkgg;v%%(4fia91Q$YKE7Yx_l4Ih-`}(U@bR;qow#3GZ~dNsY${$uh|q_n zHGPaU@h&|yAoJ2jp21=wa$2Kw0}=v_#$aM%pbfOVogoNHPAi)XU7-tS4C(zMVObw- zG$OhZ=w^mK*XLb_{H`?vh#mO_XQl6?+65pO-)R1 zG53jw`JzG25rTO-0K1F^4c4y<_@2%s+!g5T^as0fssUIjmml4sK%k3%MsbpL1yq)= ze_~9%gfTS{GkHG76tN_6SY)w+r%?)xq97DuBwddT#vkR4^3!r9(j)IemWo9_uaHJ; zmj8j$qeMBa76$$p3D`dQ3%LQlp<)a(hP}3ddwT@rC1iohyS+h-F^tTccmI9)nYj}| zUO9gXmasA?0riU60i;e``T*a7>JRFDZD+9x556TGW)1foz;+5QhgGb37)f+`02)%b zDcr5x5QH9waHF~3b53qM_ak?K3*x(It^khEpSW|JAOE}YsfYW8JI*!Z|Ciho2#`dh zarjNLJp73NrbEHeg9mvTkMag~-XccXd>>SnSA1T7?3^n>2z$LJu$mt6`eTw)ZyIAZ z8(f-D5ma~a#hSYxF(%z}HrRV+^09y|_($X>s5R*P?kj1QTse8BA0~)pJ zm8$wZ<#3F?k2F;DJN`;l&g-@3L#Dp*v9U%iFT@T=OuS~qsG&)*(VA2H8Uvti6Lixb z9B1q>++b@ubsqKxFGYnT@Rykr6XRe~pl&U=C8IR4v3k@IKJU9IXE@j3@!cL$2OFV&iN{Iap0=AhND}Z8vQsPpVKX zgwa^E;Ppa%B^A~wIG{I>4GIfFA;(gmVPi-y#h4&uIOv$lU#*F^Sm?YG67YvJ(aYWk z)NQ)p-iO!Q`-(KYm((ksjaf05saH&BvSgeQ%4iu{4tyIkto8c?r}%{A%6$P_#*CQ| ze!vC1y8nwe;aEZ0c#wcc{>tN{9Eg?$a(iTo{wFMnH~7v2I``9?rNQ9FNf0t>q;+z{ zA;mT{eBV(2El2~;w|-Z>SHXdP1zq?JXuhI;G_byTdAC2712Omx7Njx^5vMr7wD>U$ ztn18H@)4WkOcszPSdFPmO(r7rS&!tLT;%oix7cq*j_;j|7Uid4GYc|bps^kC!3aRt z>A+M7;+UXR7H9CbK?`p49{#l(C>Db(VrkZP`#V{<Ef_f4mRr6mCGv$a~tOUaR-%KYQ(AL}CR~J+5$5q+aQ@r{n+jeE354I?g6igudO_ z`vc0^1C6NE-6lP!0OMdR#sRl6I0+h{JyMCKb8BQpbi1(g)1O;gThExrcqcr%>q34>h$%#(<1Q$RLFVjsr^{>wlp9x7^kqPE9x6B6Ec})5;Yp~#U>q+m) zvltzdfM3WGas8U zVcOJN$EJ-;OdK>aZS1&d6DG{ezUhWB>9^b}G2V@TI*s0-iyAV)C0GY&si?1DFpqX= zYy)C65Lxa7lvBWi{=!rOXeicf22~6~2!?3A0L*i=AuL9jQ96^BZcOt^ z6k*M}MZ~eOQ(^jv0adMm$P;CI6g%KXfHyy zJ9!W7<>dX$h9|P-R^c6Fy0}Um)3fkff52W;H;J%)H0GkM?ma1Bqgi~55Qav5V!0yuS<{){;%d8&O6GfMIZIuq~p_QZQ=0Q*0W)grN1Si4<<|ZMSt)@ zx84-))>{%2q2Yv4Z$<|BBmNO#j0k6%D+)tF4pBxWeIbGINC= zS&%0x=H8zO}eanB~;p3&NEShT2U{_pei^72UKptiy!tplOh<`>`C{S$~M za^Ou%NA>MLMOB|jUsPQ!2uRvUl4A!0zHsfBKwyNa{pb%rdRk6jga8R&2nNrga)e?M zY1e(zv3z-lWN0{2|IOY#AMM&T4GVV#806l@g8eRHVDBscU)KJ?F;aUL)y1k2czscL zF#npfM-+ZgurFX*m5Y6BF@A4{?xxv@zN8HudhIA1X_wJvtI0I(L9F)k$71YZ?QrXB zzdhC(FboBP5D~Vh^brkgjkTxD4mKt*6d(q5L{Vzxb?D&YIUyPKXQcr!{7QHfMTTABZhRt67D@>cHg0 z!(=lXnAhumP3?E`V8z(3j(sW~Ec+HNS@B@8AfyK(lujorrU+;({kc@R?@rt|%JRDr z_iZ@t+fdv$v%9KlWAP>ua%KDBqYDM82;+@WJHxtLNgR%-&H}{JqVakm_u|siw`_Ug zM>9aNO>n_Rg+xu2%jL>~>;DmbxsW^SCYGM=7Es|>6>!@W5x0#=+WQJ}253y#umO&- z4Ti$ehqsrOZm+*irK3jH6A3+3aPLj|ErTa%Ef4k*a6KpH=P%EJ6r=V$=P*M>(atfV z97qFTZbihFg{FOAOl;`rcFGe%i{0{RL&N9)*zu1&Euq-q)9<&l!`Kvmr84MKt*xg5 zk#Kukz?IOA6rD3I-jklD_OQXI!HfuFlTZk%-oV6uU__J5koXqcFkAm9zF!OoS3)6o znR+)0(VN7Lm4V)*8rezcO`*N(WX-BcFzVERhb^)s=5w#N_RZZ#Ar))it$xh`<2WB2 zI0%ez0`wCkY}jl(i`@Jq^bxK;F&q8|aNLNR$f2tJlc0`*g&sB=8aLOrrvJQ|_b)Dj z+iTw31&e~g)b_(LE!v}VmrlYO^d%xaW~`COTSk9YiF)&OnBdnxUd)!PSK+46A0hJdCsE!5(kFfqJ=(o82E2rWVD)7lc)~cl02jMno&@5hz zT*{buD` z&txODq&VV$g6D98auyl(^N0l)EoSbpv!B+~%$)hVJF`bv0K$!mLxxaIjij**nT%Af z*WduJ!v7s?Hrc`^^T6zT3-7(__JVt6O>$s-aTyu->!d+x^fsat?Ew#{hLc>v&=F97 zT)NH{SWHPt(j+BTrNVliHLiFNs8JZ&w=G;jNz&EmGirMg7 zsy+80dX6lqap*Z>Eu@~a!{Dp_AP@RMJ_1f^ZG{{0dcG2lOO8y_mC(RBu$JoeU4zhP zE#(z(waS3xZ8l^#C>;qITcX)tbtocc)2L+6a)}6>Z_++_bA<7>HQIR%r{e4@bJ-U? z^YaM0vzWAx=-m9d7^RaZLN4_o|m5EV70TZH4p+aMxQJ+Hl1@(V)H2v!ptDcU9(;7Q$h>>5q(Nb zkfYJ36hrE32HY`<$JW7)y<0awx3#43o{p`kcz752T$Hw0cVxwiiV8?duDLlI8lH=Z z+MF2ck|h}~A%195()~n-M(us}ntqA(&=<#<$Kz&Tp~lr3|5v%s{F4U{X3?lsh9{Sj zGKqpzXbTV*SjKz#o)qYw{1H3spj}>QlG^fM3QJCQ1F!`RFfe-Ng2`~%Qz|^Q&p?X3 za-{?Cr1xmWV=qPhdBMzEEGXnBJ0-W`&x%Bl1-%Z#v0uNwFF8FJN{&Mo^E#W+2w@9;M7Ysth<679?WnnO$}hJE07*0$cpRJDdg|tC-^2cc zK7X}6izIzmkC$iyoP;O2($0jezXzjK5sL=n0$=v8qhRXrQcAB^L;)%mh793!#(yKin`4nhNno60J%hpk)*A8U`h| zaf<`b_u$^3X)zvu-Sc`DZ~-$Ruoj5g6@svMZi)-VVa>UiH6JT_J8AA)A$8ZXTU}bt zE(lACdS|1Xxg>32p~0Zfng(ZH4TjNo2=dpUy$3YGL5#M0X3zfp{6`#E1~w~3Xt^OT zZ~DT5-`)9pOuuq5exo2%1o~ly>CM)LV;Bir)&eG9Q3`KAvKc9eXtCS@$1qXUD02&c z6X6&jGDNj1#A$9Bf=(N7uBv={8}ia(nT%hg&_1xkOlAwp6(g?a?!-v|2 z4o)0AkOC51EK*A?kf#Hz$dSpTx4 zDDj`lJtWld&A1!Tc$fuu!-D(H!r5>bpqZWya1#p(rrnU5h|mimXtUrHSOyoD_xLf& zf>j*bSPuzD10#D8Ri#CXotz|~#ETGn-=63-oIqYJqsmGO3KZ4Ro}AbJx*6d4OhkE0wF92 zV}Q&*T-r2T+5!YMFJBT}V3Kl4O$VEr4u1c`j}1RIH26Eu<1>RHikKu~iBuH+JFP+5 ztj=Td9R1c0ggFd^Og&sdoY`sT5NT5%%qieL4#pm%&KltMje2kZby06TRKV`$Ntzn-Ee{%Hs3M1Sta@O2zQi4E6m1{kL%()Yi z)2Ks0PRl}wHgq9v-aHJki(zINg979v|37C-@`q%}mDi{G9yX(9(<&0pPS%5Eb@I>) z@mcRaz5WV9FRJ?_EBo+=Xb;6-q5ipIzbpIl66z7;Nlx|GBIcf7Q+8C`BkEO+#5rDv z9wokJVxUD$Ky=bplsYz;4H^`wiMFQSrWB9pd18mkY-S}5bTEQE-i)0iqb~Oil~kmL4E`* z{%1h;sl;JK9VPOaqh)-|bI-W6l(b_otic>tKDg9Z)W5yH9g5dG`66~e>E9Mq-ReMn>5Wq{wI8sVS`g<{=w5I6ro8(O*e-iyB z^vg`q4If_h<- zH34Pzu;#<2rlx6>^VBVi%`W^8BPP5lY}SkpOXFoh4@y) zta<69{dte?e;+2=Kgyh02=YHEy>Tt62*$LOA%ln5Y>4TC@VoD|W4(e_KKJWkYoNj7 zZN-d5SWbEgrsCh6G3^0kQ1+klo;u}4XaXgDE{0@-XwpSPOVjZ&{jL42*41yQGhsCD zRvPYXBJLKkHWHtxvKCx`lh$!cie zNdjgmu}av3b`VFRu9R6Yn^dZy%?{84osJljQY24hxJ^dC4?({ZgR~X>P7G#xD23*K15O8Jc4KrGi_WObv13OV z_n$GRWV%gQww=Xgn_qkZc@r3$R($O_{9`+Fj4}H??&+8xwU8#SKuEfA49YUvjZf;a z`0_@hDVh&Oc&!o8K2#=xhk)Z&IitWXFEf(?=B>MDP41P(G#5x)$2!Vbn#4e}S0TZ{ zW#{aY8&H2mUsuKv)y1m7^~}K@6S2q5*rQ*q_gK}&jn8a*6Xo!B>5Gc)8|kfWuHBJC zRMKdV^Q$-P%b|P_a3Rjp?fkl%MUJ9|&BMCJ+w8aLYn#bNo`b@}M0%bmya<3mx#)_q zrcW4m10d%6_U$uIL&*(G-Y zFfv>4T}kv~nVGj_rq7;}ZNKfIT-q9u^?WXwPvKLr%z636iH|XwpLN3^kPX37LcfSD)&&tR%krhq^J6S)9Cs(sXZV{?mb+Yboz5k=jknn}*8e}IM44@z# zk@27diufyFhIoN(LX{p)%Hj&SLNG|Iumdp>ZqLUYLdh2-6&%97{~V^?Pq_c5;ga*B z7x!XSFOIv@aE3Xi&6v>792;oq_ZVb#rN~u7X`3A>iNG+lrr7P{)26@!8vq$6;r)~b zf^hWQtqU{i;R|}K=O*G-@~IhiX)=E6MU1~KY%IA|x#i1p*kQEKS*5<}%~yBq*uEW{ z?JGe4znq%Jxw(8EISgNeSDxBCKoZm=wfeC1hJ_zISg>OThgl?oRojbfkrOSVnPZ$z zFBnaIK=^C5;;u_?^b54N_zv?x+abzrOc|LBu4-$;+#%s{Yn*hC>GaFNv$I%|*Ab!oB$f_wo~T{wHip)NQ_?>Ebu9 zmhDVL3>d=Ejm0M~fnWlHFEQy%rPG;`GVS+6>%d_H5Q3z)!rU0@t$uY1I`JO(2uRcr z#O7h`n%An^!9%jGZ_9rF`Ew}W-37N_xV8QC>9YaW7mK70NJhdox;214=~wk_#sJo^ zutw-cdKTcgF+dbHH%lsel-Z`n2YR`_qR3eB$%7N! z7S~vgO2CgoRzDnh5bB&8-HrZt!uciQK5x>^8;Gcw&NvzCk?Mt`+UuL(^r+DRw9zqc z@n47^k_7oP@WMMGOkrD64PQ}zsEH7{vh08hp{H@r9g?8o-GKq}23#RDsG6Avty(cj zp#orDZiI!*^NH~~tjHQZX&_edfr-*)kq>r7+&TzY4=M=tTFk^OJ{Z?V)*5+o6%8)v zU0qS#ArGsmV*do`v#4n{7@F}yGMH1A6y!THQx@Bq6Qgjmh$J{os?P@}mJekA7Mwp$ z2Ou((iIG`1SRN!87mJ;CZnKF0^xDljv*iQwdfCBsCS(KW^$_DnwPi1VauZXP#}eW3v^D1AC8YYd=+TFq7U zwCA}woCcycFYl>4u$%@c9IUh$c)M;TJigL$xjUS8o}sg>(p0$=92u&o^-fq@R23`R zu;r&1IEYf3U3p-Z#z*pAVt7Y8hVCx|Q6-OEdsINKs+oq@2R$R@d&tKUT+S1@iRV2Y zlWD|^C+TuZkmswWzLX(q(0t(SQmK4|mkAfzQwK*IjIn(a6BA-$fJZ{jnGQ{&b$Hs? z>r+PfPJ3IR+DUu|1zeyFYPv#Q-4ZGODAVJ@ghtfS4c%!--H#Bwzcw~B9zTm(gK+&_ zZ2h^hVLxIjhZ>TSN2f{U>dgNOdX!ei*|@7ya93}`Jk!jssF!R==IK~iwkX^O-cM@6vxh^Y9+^y{ z7R`hX!`#D6b%Kg@z%WaQ!?kpWpy`obQyMAeN^uYGKp-K^UgMX6`xq0i6J|0T)(G$A zlPH6kQiaV*xhBrX9pVmh-*7vzmZou0SUh)OTrYu#Vabw7h{_PHF17bG^G3A3@Eg23 zhF`9!p<(}(unJCb7W>F+cPcU zVMlc8S;c1}-+2ZXa-k;WyA}oZ1Zk3O2Fn)gkMqTgkQ^k?q-)9b>*|=ta z(iejdyRxRH5@C%(SYCqD+I;D^ZK7cFRag6@Da?o8OozT)PXh$D$J6?DpWg>H- z9yvi^L4O!8m38Go{PH9CWkTr^kI^vva$gJPFPYEuS9wxiu)Wh*hr(^t2LsTFh9rID z;AQd|Ch%rLdD8q7oJOo+GCC$B(of+u=@XgH1JybSAvg0bi`?0&w}0Pc~q@@6mN;$D3k1u%j`y(D!UPuRjv?(#%mR!p=Ka&!|OFPGx9KZ zpTkX1mAscRyKtFGds>={&LFd{3WDe~rV?t(;#sL`LzqPbgVfIf*6jJ5h!*Qu16-w% z4FeDRe1{JmsHy(^z@eX-Tbdg1de7(I9cu8lwlu(uD|M8k0I4q~~9nHZ#=;b@2dq8|gmOeD})Cb)NwSm_WxwSS#Bx=|*YSFA> zXv#KyehN5{YEtq!E4q4y;?z#U^HR;0{rk7&05nLh>^n6t@8AFO?-UCNNnS{6tX4?9 zVtZWP(O9Q^U_gR(2nM826b7Vn1k#)g%tt6vYgGsrCZ`X9{yr?@cDT`Q&lrK%Bha^X zN}tz(;X$h=5_6~vjX_9?fd|h)WK9rbjD^GSgW;!seg@1(vc)1RMM*jX5h_X&ItPBi zK&Tt9yRYilUcxRAG7KaXk0F|TscJnb#jgsA00#1|n36#2syHDXlqP6j7nC$vy=y3bi9WU-?;OiBH9JtS^<;E4)SM6=bHzj)n8RZ`g zz28jJZfw|9hx3hOz`R6d8Dd0xT=yL|v36_~`K?u3rl<=m&Ih>XOW*cuSHE>!{+6=$ zNwo?`r>AFT4vDoTpu%%_60AdNby$kaVwUBmr(|Trl8*- z!;7Bgd<}r^Om<)&6?nLsjTO02m-KI6kOIOgm336>bo%{=N_uEY_-FStD<4NU@1x=| zA2eS1O zz4n!&`MRM79qcui)L$mgFpbA6w{>#$7!F03k=TeJDG%l7W3UG?5e>#ZRELzDXpcMXk%b~WRYdoh8o8B|j)~lfb8fD=KAR)1i_OAaG3}>( zIiMUfva>7*O-c}5*(Au=NBqRD4w3um8%!KU1=3bjd?So|KzYq)ec}WG5d`?!m{2It z&~Wg3k1r576ADOfO*d34k-MmKyR@mPgP;^)++1|8Qkaq%w@STk>Z2>;Y~%~%3lI*3 zQMn9ukGr>De8Xho;7=mOMs1q@4)VuGppbZtS5~yyOf5!=Y>cX3Uatan92vCMcY9@ zFd6<7a%Yi4vlh$D>pF1_oPB)sl-jWvB$x_T(jS2#<+Ti)dC!C=Zc%% zhSJiVuZP2>&#&r{C6_$yN}787@;?;%{RMZ!)t&`cyJuJFN+n%V;}}u6Qqep>QUp%q zF^x^l-ch64!BOgugfGY;jR38fk2d>!{U}vuP@hov+{s_yWgL@=a09&P;jUF6{{(pJ zY%7X^0&KuMtwDMHh+)HrB_r9hFY?E@?k-|ChxyRyIcBvrdf<&egYr*tNyB(i)8vzR zoB>cZfK+qpN>z;x+KVwj{NE_y0CcUurb3#}Lhj=L`3Kz7@4;1ZC+JLG0BVXPJhg(z zJCp1KRKQ!sh@?V|)cD@_oDQMXS|4mNf0tUH2| z$ezR7dbZcNz8`#l7Tbn!#xgD$t$?{w_w0ttvlbq^cF@zHs(WOGZqxh6I%Bf2o5U6{L^*YT z$cpJaRs|j!d>eT(?nB!8yUHCqcI@`}6SD7-s+~ZnS-}9>*3xn^h&f0+CCTAXL>FVy zBRt9JOfkAh7t%2!Sd%~!f96C>OIuJdgI%TC8Rzkw0JJ{dW*gcMsfhieQGYyiF|3Qy zV|j*)P=^>Ovd*IE%Y-DXT=Ct(GbetLs#V!^KgO2}VySh=erm8{?}>Ilw{`QDS6;38rEdm0E?tBVh^q%5reeu++v#0t@4F4; zANsiyNhBlZ#Jjl8V-iY^)(RZiMRe;2M-x$t}QVW_YR_ zVUX;GV|=3~?^G4Db1UGCVd7+n5tX{N1_eFU_iZo*sL}f@d|$<~TpDexCECSq10jpV zfSPjx6moDEE^OC-c*7s80;H7g+6s+(Qwc6jdChI80+HfGOj1Cv~+h1vpT@);jprnk4vRDoFgn2 z)-DL`aJBGE2L7U}3xHi-<2l|QWXAr3Y=iq7S)lc}v<_X4#C{i;0uXj|(K`S@)pu^E z&f{T3P9dU1CuVdj>_*L4%OxHhIQn3YG96Er|TS%pXk5a?##{J4u zf<)8gkX+l-+OaN=f}``lc<>U=lslCs&ET z0_#YPQciF;l+0qi&!@L9DXD35=`Oa^2ZQx5_lj|6$-~Q0wi{*{$U-#BA1;AZRc5>H zW!kRgqU+kWnvx}Uy>I5Qkt0X({HTms_j43d5UAp0*B5&R`k`{X$);O3Jz7k@_r>Kmar zJv_SkXuspKl+TVcx8`=_a4v)rF#r;yR#^;aNg!;?&sZM5#WInBMvmR_+)yqK@WyzY z54nAb05~4a3=QTQ?iMZ&ONb3xcmar-+bZ1XBW_U&q(~NMofzkE+y&{BbkMKE#8)PUt)hNK*0ShzV?$;^Bf8I-Mkjr&5=L?+>x;Y;<(}fLv)-u z6pD?Fi6urP*%%wEYbWe<2prc!Tng((_KQX%K{P}oW{2X3^dQlRIu)R6(rGkI)(Y}A zV2%w@UOepzo zDzXATFl1wQcKXj@-r%{j6E*#OXMwi=^hGD>-DF?#E*4IETkmb*iUo#HAo9bl zb#W~R5I&Y%1-a2!G)HI zd_e&UdAzJlzFM|clbP(>O(+RcrR>7$%E}h4OB=28CG!C|<_Albg9pEO3!2Tx$AiMa zN%t%$oS2+!U)&?7wf9~t1b_UyswX|kJ^!(^(-I7Jwjzw*dY%uPtOF7U^hMESjmrpK z8zr()e3-icx(*c7O2qx_XVl5(Pok)I8$g?+$Z{O`=#Y3HiGb8YxfdnqUqX9KLjTS} z|56@je#DA0yFs}~E|llV^T5tiB-bhJ3_O2n&~F~*w`|!mNB&%CZTM)O(!1m2g-Ux? z-Vd?qQ4Wenm0-j&6VDR9yG!lZk3(fU-_2OmWh)(_n5=*2cWw8za zu0p|mPEK?32h%e~i?D3jWg%mRFv5FPmAdi^PHMpMudCmIb)wqV*6H z;m%X7ZRb$Z71$rW5XFU!s1N~cUng)Vn6SzNL|CJdNsVtN*ifg_cD46XfMy4^V)Dw#EBRrojQ|U2L5Pb7x!7{)KeKUZHaqmS z#Zcjb6gpy{c~sV$G;&tzbnUH3MeA`^!Xu8t=a(f+bdQ7~ZzcI1k^cZ|iqO-{ibcDO zoRwC|%{M@=RL^oet}YdOU5~ji0DF;_mmIj|hyu7{ADMecJjMi=6lP9y+_^Lx6Tci6 z*cX{NpMA1>_wE)OAPP5)9Xs}>@pyq+>cm2v1~dDC z$y9LGub~^FdHOD_7Sz52CWRv4&B~CWz(WQ6fr=>wG~GM2X2VUp4b%lOCKIfa_(X6b z4Yk>Z3>}IW(l%J(Yw3uQ)!Wp5(Bg`;J^jGripcLH68o3PE&&Pt`z`IvDZxRMf}TXryKuuV9o9Y+Il5tw^{xwwYsRX**6GDD6TYN!_lpS&5?^YwTgq}j`@<<=@{4G^2zE`HPb8Tn7#w7{ri~6=R%lp)Bz{B1gN(!k{u=TU~gK3*`<`QT7%;m4TR>3f&=DECoS;p zsL2xK;|C=Tisym=J6t%0dNQpSI3;!c9rfOm=QhrE*`Ke+z3=hdIwGEk&B-OJ@3}?D z{wsBIwHPM7j4^ANPGoT2GQSAM={4#R|C6Ur<7q}uhBx?R%Btn^9i%#_|7aE zzAjH!&)C;JnNeA}a&ekVIC;3~ zlwK6{rfK(TUCi!B=8NB$nVR$V)sh;=A! zrs(W2T={E?6uVt0x`uD)jd#Sopl*j9KKxbgGrmaL4RBBrFUA>T^tuaPZwqB&7<2+a z?Hq?1q9P@P!dGtQG*~~ut8mimJ=3kzn~c(KC)d&8iUqKMkB1=bYxMgq2}YNu>ms5I zny&E0E?1l(!Q%HD`w|JA3EK(SiF8@w_@|YPB3!@a_+Ov6*}OwYt3)`XcWHz5ZDmyv zFK)LRW1AuyPF-0UQ=%JB>rvpYqqEa!l%9@CYHv@XCAABHbw{Bcpym71K6wDSBnUp3e z%iS)lv`AEw)@g*K1jDLzPRLU@VWoBIoJDacpaj*`oN6}HIvKTZMAPb)Q3uw81V=yW zz?P0tzRmk3KCQhFa)6)hO|`QUC*tiQM=#g%x!znnu@optYoUb)G2GVTe-KOb(|Q!) zL@DC8hHFzthh=8Y7V3isgKL&I}lr2 zu`Qi%49?e-iERCkJy@whpoN77h2m@K)|yJ~_N+nXicde-UdlixX{f9Itaew$KdWjb zhoXN}!OD~Pr9}@@VcpkJg>wUH1ag5O1{PA~R`bZwbc1UZbVy*Y>hz-ytujh><9t8C z8Bis(Y@ER@ID;v29UK*7q0?b6cMOW8{x0O?rIaz zSI1%bRb;u8LPN>EoMY10WLt(Ka7JhJnl*kp2|~@IB}vO$ms1^a-3p*5WyZ-LCt} zZ6qxLMFrvgn}a3;%z1+eBtVF9gfC*)T=bsBM`yh!AmmS2$cjsZ&kruZE4Pt8R>zgY z#=&x9u!_~_B=ILCAbfZE0UB|r0a~MRXvVm)lp?-Tx8~_}>)9PxKpsb{?I~ZbMfq~( zI1k4q-+-*IU=aMvL86igH^SH{mOdeDR{}iii5b+S|`uAc7AQN(1`arI*#~--&s(3)lZ= zT>oR3SNGuhql-~`8l}GCto@8|(1HWiysl*X2Ms5KjPk%L5AGYGua|irby@QnZ-VB*S?jJB~9uu9{@iEumx$wbTBl~KtowZ-=-TT?D zceZW$0<~%*D2#Z`C<`bvVWuTmQisGDRL}61)=`$p4hF+RX_7G6lG^?Pd51ChP|6^j zVvFexyKv1kfp_q&P*ss?|nS$R~^ z#BPk)dTlPgTMY{WQd?R3BF~rHnwSIrq~h4Sk~jVaQf{jpwhy;Ldn;DXv1)8Z-)H2 zE%JN4SVJtvUu`CK(IWKQd_>x&L`Fw%*zoGV8jp8el=(5yk8QynK4!dlEndoQSHbxYuhCw#Ga91o?q(#;u(Q*Rdf6Wmq}5-NS(Z@Ob{t zj*jyT{4ZG5(d#-bP)#PYzy~fwyeC`E@}K|)PB_slpq}DM932`ki>$#R-I`E0-e@C% zcDO-YIylT)P>WO)BImszHWwm*l0@7SLm|KKbjO*CoKEn!1Xxs*Rwsl&=P#f0h2<_} zQ|qI;#Hi0dKGb}u@n~bs*R7#JX~=+T5#w{KOLn~VLDhS2znee% z?m6=uH)TtGYHBlca&ONsm{G8JLAtfA?&AtrvRwTV*tUkpZ@^3mhc}}5w~Du2&Q*FP zr`eV^7;_sTvxXlqY?{Lc>G^nLAbh^HrS;UQ4x?UgjM9Q1)Qq^T^1#VsY2$T*TOYI} zS&(l57Cn$Og3}$YhUe8;I15r}ijBd4#3NfsgQzQE&PWQu+~5>5pP&JF!3_@BBh$Z}_Oc=l5 z4{Sbs2;fb#htn5a8gt)xSZ6$i&E?fYkZl&+SKr*+a^_;#f2!GYczz>CxVfWp6Ib#Y#2vY^gm%#`lA0S1I-DiFzXV9M)<;yiJ*%^F<2cZ z>g;x#&Rbh6>2(YX17xo#APd>jpbLk0d-`QEFj!27QSdtO_rz&I(%^l(D}~9wNqSa{ zraQ{0Ix~1Eka`AZKC%rWqR#K{VnVc0QZq1n^|LFp&gdin79;lqphqZqEV=RQ2JM;Z zeHn_Y7=Wu7f~z3!3t4oiZE_St4)y##LX{IhqeQ2!w$`h&+3omkozacUpsV2Zy8185 z%D$+_b!7JQ><%-wVmg+n%{cK2TmW4LUCti7dx5Qxo;89hrkm;G;4|k%7NwjD?&C$b zo;QGdwCkLoZYwYKRKED%Tu%WGqQigiaVR<;G;T#lr9R+sJ>)B6sS+G226*fxyS#<` zDs=hkEV^=g@>wa{FQe_#)$O-pOWHczy9eQP;vhfh_(tqO-W^UT%aYf2=R?^WBZ|;Q zSuk5j(=F;+;k5JhRQt=x>w*GqgX~_jM*U?%MfG~i=u8!KP`C`M-Ux9OGa%XBY3CMS zty5$0GHuUq_wM6%i*T(et|GVE2ZS2@ZCn6YD0^faA_IPA=YHeu*Zzm2!dU48UJ67z4U)`>@ZGyI2{}3&n@}i9Q!}{*4f9y@YANb8x;u|4~T*v3r^JTK1yI=3$0Sg0j<7Q!uj7@O;(Uk|R*c!GA(rL0CoN zy>Gpl`6#{(_*b`DkO>Ne$}!0M#$Q`~-nJk}T!PXD1n|}{n;UUDB*%dENT6Kr zZfUF1BRAs;N8<`9=eU=XLW~U@sci-TTGnd(AwzmP(VHv-dV*+XS=a#sX^IkGEf8J^ z2AkVl8hLgwxJvBfKNk*Itu|YqkhiVdVp*~z3AekCY4M*yopOo0SK(fj;(kNbo7u~+ zD!mZLdzuiJ>I6d(>gz##ixhfz;R*^~($m(dHK*zPC*k~iv$U*qha7t$U4?&G%0^`06!Tqqn7VHA1^jT4N%p z1ad-P)#1Nn@f%EW<^&7VM;rK{&u=z`S&~)Ss?Os=Tst9Ch^?3KX}99q&0+8Nux$^u z3W2V8)c-3ePzGz?S5FC++@(lHD7rrU_4XG_@}{L|kfA2VpX&(ngf06ayF|%>ZV`QfKLY&;_xw@z=#^4e?&0J& zW1m$p#mW?}6&LnfMX6-!A7#HcFdja~c*w>tJ&0c--w-REj7cz~$ET4QdC>5D$+w4r zQS$q9P)7)CwI%3?&;2scBd5Id7;>bwF8CBU4rV zR@gFcO}*oF_lC;-`~G#XesbR2c?+hFn;=o~&NF}--lAml+``?(U5_#KEqjH3WXY1L z6f;p^CNuE09xk*a>^<%B8Tt7$=FfpyT#mzkf$3X}X!^_S6D71)$$iAV&b`EyanB+~ zTgKJEpXBD2a5E6`y-``>QbkeQ*5qd?LtRmA9$#}|c)y2#*n#6v`x@D3^O^r3MTwLE zOyn*M2IwA1h&sU)M>7LeV1qJ1j5gcgI57$Idmc}l*C22V z9K^B2kY!LJ=)}P`8Y9gF$2FM&TIH`UVL`fYZ^D8ssQgYo zGhspWu<+-fhliF)~1+-jU6{`>@f8&o2vx<9B|Do z#D6NTmkO5wC238;Grjw7zC$99PRGs%&NHB3)W?EuvZWLELA>U|7atC`A zmj8>c=0`CXOg*Tj%xvchu~+bFy@fQqSezH(?sd37guBl{&gBl~K_U7zIdU1Y-Zsl% zk4htCJ*TA6G$KffH3#pRmJ@oA_CV9Y)2$wf{QnfE;}XUI8OWAN8o$ZphaVJNI{

~r)`)Ni+>EH+$+*%|g&bIUM%tM4w8%9ziJaL?uPheCdxO0(e8LRSFJ76) zsA2~AC22{tT=1#&1wH40-{mThIGD#Woti3B8JmJ298pYIpz_dAE1<9fsF5t;Frfr| zBIi@WkZNq@DfHKq=&wKG>VA*@x(ina*yXiw2X-Mhq|bu>-qX-t1YLX0n_E^p9eLrs zyCwm2*y|&_iCZ0XK)_B{#frvN`Xhsv4_HnmowIMro)hLwp?|vlCR-?R030g)$E2k_ znu9xA3SjQ@+L<9+VAm>?qSNo%S-$IEUfCpB%FEyTaMy?D4Vj}7Fw=uUZcO!z&ERiMs0~Xy`b;ipU?!sVMex74AxYOqmg!c% zeLQFwvy2>pu7s5jfPd%KyBDmuFWC@me)pxfO8zkkvHS-jzei*uB{E!c0rzcP7Zoc0pWs2f-Olt z&tx5k@*4i;Kp>=zj>2J4c?R__L%$qo6uFjG5WIGY0JU(xG>Cjh+Xd7FieWlbO&u9b zF!zl$1eh>rkV#|K>CT*Fg0`!(v-O1M^F!SRQ`E50mPC`)#C5L%g$vX3qCWKN;j@ET zv-i?LkNqg}zSBV*)TA6#kqW&w)s@~2mMO6e_+wOz$>T85WAPseI?5gvjOE1tIF{{f zBZSCSX4ZsOhFA2AX08OIhjIQ4<|pov2IS~Wfl^A&>LdtTs@;yMqlcORJ^zBwFj z_6CFAp7HqWG(G=9>Rx7`y&ZQjO=W1jw=3n|L`XwsmaK$e%3%o?Vza-07{t{_D zy@(+4S`g9(*CZiL4aCgEZNUHpf)@fv8#8Mc)&3wBZX^1GByg{GMFNdGxQ9b5jb0Tj zfums6D)_M^{1K#8s$`|U|E))X_p?$Fb&m1Fcvc_k){r>tGtBFI@jEkdcE7{fWyu+G z1_aQfk)=IE19iauekre$?~@nHPWdVD%h?clTSEQaT@Rs4?LOHo$H-tBhS&E(x4%2s zeWtsfzNNo-wg4^nfc&t$LS7>OMHc1dvWxbE8>URu6ZN{^U%qLQa_eTJ%cp}(&)4qa zPkvgSjbF1tFpqa}9LlP`f@zGIh_(SA!k$E`_E)F`7@iEpthXZapAbQq3A|e)HZEoP zJ*w9vZW?zF())KJVqXdWc^oXyt^C~l{Clv*2}+yFFXcD$Z}XLWEx(ud@D2PaI_N7GHpH-L3oxK8+tl`#J5JOc5{&@VYxuceiLV6hk#pu^PJTUT)MN7e6?! zNYowffN;AQaG?Ks8u!9s5oMMCz1Xs(A=Y=59KM#oFFVW2-B8TbsQ=&Is&zW&WBvFL zh8Z;&(LHEeqIP%{-#D2Q!*p5?Kfo{qB~X2`r_GBzCVi9&h9#7|^|c=J{Q9fML-jsc zx8Ha8s5LqS#wOW1aFESrwSs%%EclQ_L(9n%%}wCJli`X8ucvHBl?ehKdx>3K$2hk$ z7MuVA@<je(BFSjO!pgETx ze?u%lkq(G*K;L4>+vb6LS9v1h*1BJ+^)z>M0%+?yva5VOq2ko`XWjF!?+Zfj?-Ak# z*Xzf}XQbcQS}_Sws^0Xq`T7rz96Z^-b4e?6gfR(VzoXuiB9_la764trcn!S->+=9Zb`;Coj(_|beG62-I$9A z^Pd!%wiG5m5wkAkwj6e`cVNlz851y~tmzq9Lx>*RCzISL0-39 z|Moy+kI$cTpJW&t8*P9RIO3jM=7!<910ZG$jvu@vAJh(MQ{1CIW4XEOTccw zXr6*iOJL|yBf?;}{^7}~sn?AjbAtpt(BE$K8KMEcj*E!`ltc~&f}FAAJaD#A5r0R5 zxufmZ=HspHM)RP-NeDHCJAIw!I?wu~)u?Y4qVSRs1SeV8#Y4Y_ng;riuq0dvbes#X zyUE<@g}0-J)0Awo@)Adnc)K$)i|a)7`R*g-5_n%Uh`~>r0qglI<^%m&A(bsdD+11F4wzyH z#f!0oqKK(^${Q?F)KpI;QH1Whtuf}O@_m0F8)(n z40mK zU%0>w+6x_$QwU%0rQscg;Ptxw!*CC5Vg&Pa~oQRjyF60%(3S zO8w#?;LIUFVD}(a5g~5}@~(M)6LS*dFL60zDP@B&28g$dy#i1W|6)@G6-pg8O5W|U z1>QOgTVVkv`G%wHZ=&!}du`?eqCP3Jc8ASTw^QUh>oTRd#q*Xdo}FjU&$Jg5!pAATV9M~;l+`(%je;$hw=^f3z_=q#Au!0C!ks=Okw%S~xUXt|=Y{jFKOg?A z1l-k26pLVq)@1q)8q^dx1OGstAhh|k?nJ!`EXxHgR-JfIvMr6w8DO;X)zy3db>!sn z10Q{`c}wHb@4nsl>BswiY2&4VDa)6qoIU|Kgq8(6&z);NasG#|_xf7qg@AZf11j4gYOxH17#PPIfl+_YHx4S@gY>UCo;p6-quz> z1(}g!#%rM>UhX_;pmEeF}0_V@4_mfmUd%g`k`faCi9X@HT2AC4gtp(&CVd^!i zG+mCKTZ5ikh?ztz6KUu<(`17@)IN3E)ck4F>?;Yiv3Ta3I~^0VGIJDX|3OwFECPlM zNgpNb8+_zsPD6q14E(F@LksLM2(%+6&id=}<&&V#QI60KDEnK6u6$_X#EB^h34Mk& ze+pq;-5doNQdA4Q{LxK zBDd@G$X|5sO+B19OYyds^Jc!vc{2xy^B&Hd(KaeIW$a70p4GBh(YGBPW2 z-LkUQ+L^h7T2firu9YRZWNfiUW-jZJ%bFD#8IO^Xv4%L(q=X|5IE=&0?{n@Q%-Z(% zKF{;MT;~6sx%YnmoO8bCe9q^5Mej?mGya+J#>Av?>6ecmpLO}z^wd<7DKL)|?IK`9GoO??ulKRnW_qO^z*^!}pEa_&(lx|G=T(q?_i=TYMj&QI9?F!0Z_05`Wk7@yDNhEZrdM z@3SC&i-ymPHlo)#dx2i35uMq%3)E5N7&A3Bf7;|)HxH`nKCBl?@g6BHGMJLFs?P;( z#|EQnDL%Oo&Kh`s3NU70l&`E7k9J`NOvU>OfO>mbaqr`Ced>ddn-AvXPDw+nh6$%O z%AB4$5&n@m3qUNFBmEW=Rj;UTJTfFD$~orj9Nv$LydQ=4eUbNlvh4hrG_AaC2WOiy zh~B`!A_lilB7^53{2Yz)_5~a_T+PJ(p5vV-PWPYJ>eK<>>0=$o&-w<`qUyZBMJ=a2DYR!`p0(o7osQ*I?XG{i2bO7=>U`6FC9bc^Fp3XUK(j zMBkL+PtgmtZ=dKL{{VsNc3>zSg&XBnDS6?PalKGdP5T;9Bj?VMy{pc-XjL5ciOkh3e`*ZLNB3J(ZzUWaag;l+2PR9#!!n zSviwi4|g?O&~}#TNV0&!O_7AC$~Ytm$5_ELF5U7!k$M=`Se-&Yy=ph{>UhJ5q$Kl* zI22eG1W*z%eo!VfN&;zDJZ?YzB_u?iLufrk&?@CmtB2ok2;n#vEp`W5jH>96&prh$ z7BjhF_u*lgQ|BzW^MU&wF1KNU^&`Of@XCq5e_+YX$=7A=+?j{*byQ0F&q4*T5~SJ? zxCD)-sr^gMOY1i{$P6H@@>|YfpNqV|BrLASLvvhHp3VRP0vG2r!UxWsg6mK+Fq5UmOx0E|o&g~07m-J831z5GV?8}EM5@PRe_AO#IY zx5YBjXuP@%&fiM5K6=%v+}!jmQ?T>%58ToMoB)Z-?U=MXF@+DJsXNhdghQZAhYJ#M>!5u2>{MY{^bx0t~D%hJGb2^`z3q`VxK}%l_@k3`Zn~X z9O6C%ik`7hfO(Kvn>X7+Eul|CeRyXsza3;uFn;D?V#VUI9NrE%GmwB%IN3q4klLuX zzUT(fvWK1RcY@n9;OY2tzP#WmrAyCYsFV=e!r>tuZUUyqxA=TuGW21?M$V?sJvXdP6oBiiHfqaX|YPB7#$@Nk7< zkq{&Jf!Xh;Dw_x!lbn4OJ^2cHvJi8PGU+lI=2~q{O>HnZFKh)Rf{10yQuu0fVU%j* zEd|dhN5C4^;d3j$bLXa6AfYC1wQ)$Nqb(h)n5FPa{c-0`oBSOV9SO&BJK;AF91@q+ zDueE$_T#h!pO+1fb#f{&P5=hV2QOQhuPkAN<(A-)&G|GUU_NYYaqw<$X@$D5q@++3 z=Ru7x`*C6|q0dw=99G%Pz&*yit zc{Z59uq3d(2>S$I;+jkqGJXtvyObhWr&_ph31Bxa+Jrla#UM&vn?wh1@(C61Dg6)d zH#CKk6{oR8;?Ezym4}{1N@nN_4 zkf$=Mp$vy=-n<$lC{iG9q0<=nswS8*cgeDcSK4MG)+!{7G#Kug9xJ;A-s zeZ!sSf>>ikb5l7N_XXE0Zy~(v=32pP;K8jKSyrpC@?zLyCVs1&E9b3NS?EysW;zD8 z9KLbtqq55iCiy?8+i|q>gwGkL4thLoa6+{A55}ftPMr`pJjyxb*56>4U8^o|xUikZ z1G}-VK;hbrbqDr0f9vV?B~B_@5y|sNx_{<`3HiwGxIQ;CH8Dz2r7*bMGQ=NJ~%Akj-o8#QnoQIKS!6qyGe=65UITE-HhNq1n*uFHlm_{$tZ+H|0!6(?jFAw;^QO~8PJdyKJph1 z^AhrJp)?WsN4-Iwk=BzoDH85MT;FP3A63br$jb~!B9wCowQzNHx5>D@sqn8XSBiSz zekH8@9IU98;`vP2D?D5|m*RCazN66HSB3rywD8j)9!nfj)RfJ2Z@uwls(0kl0_17S zQ8^aip2FR5O5!1*R2uB^A$G|>S!6DbWd`#IV-kw zq+XW6et8~AnzX&m?c+Y;*4nLnhC^arvN^s7dTuI0e+4*SGeW9LR^>d^`uf%mr}n%0 zx;oE{@;qrKYoU>c`-NBzE)1^__9RW6v-}1a`ENtC?1uMdCzd8<_{D>eY;7=u8q|-> zv5xrcX>0fnsX`lQZ)#&k9b7X=ZD#_Av%vNbuYzkdp>$$>osvBM~|`?MineZ z?usy=iV<`w(U5?$S1?Q*qBecfSgfuCV4(H>2sEZstBK-65F5NShFs)+^W2**IRlUJ z*R5Z*eRRO>_4WCJ-mtp+M(07#0l2C|c*5okgM-pa1XUchgm~i@uv#v)g52Xa20@>f6!kfZVxHnO^ zOR0eH4trWRFFif_`xbbl|0+)z)=H+(>r7Hv-E~hYRhy=S?-lA{#GQ3&)Pc^fa4cX? zxlwJ#DOY9Ao+NkxL)#Aq5(LKh`_u8!?22oDz2u0W7Ph7sh7UW}w&&@}%4h4Whu_^$`OLnf zN5j{`^qPm91Wi$6_21Q#7Ue6`FyV>o4DblREQn1rnX+=*8b4~;|1Xx0n71{Yr*buP z#7R+B6v;Yx@Zj)5*mi#fom+?C`F0^&I4v~kd*Zvb^qamkW(Ow^SUGl;L;ET6lR_<3lFgB)MGVf^l=G@5a z0=Rl_n3|P!{hb(|H|HdD)OUq?n`&kN^p%0>Z-ukxF4Z${{j)=QD?{Jk+sDLM5FnUH zajK(4unMSDq7KQJ{k|~k0{+w&P$4|Mg$r;S#`cJ@+Ccvq45}YdUV+iVU4TA6*WItt zYYckcJZ6kh6#AvNWG#3DTD}l1PqoWOqvg}l^43tNYWMElug#UqKwOQm2J+>Bxf_R5 zOGfosBZAwVkfA0X(Nb$@Kh#}tn{fulpaP&l8D>5XmLA_pXqupquCbZF=XrsH(MzhdH zsx7r2U^OErdFr2j>$Td~_5cW6x1n~64SH0Nw}Rnsy~ZJ=W31WcHe=Cv*6rR)$-VOR z=^THls@b}`9rI&fAwmX9WghY=TcDtp!qzEtX!q>d^TDTwJpn2D{HY_K?cFnP-aP%0 zKPgn#0BGo{4oA(ydiz*<_T71K>2HhE^Ml6h1q%@5ntH>6qHO(-y|8#Uv6qC!*<(7k zOI%s$4453xDGM3)Wb~TSvVt6VzAwMFw9E!T(KKkQX{?DaD?OhNR3XY^MF+}@$A4bo zlD@WBZk_?`IP7OM^j5YZs9eO-RX9Bp7ygCxS<9F&9IbYXhNpZ4+Ak&%&=iln1KK@CFwQ1I&emE z0=dS5gAyhz2zoZFxzHf+1(F&v6qC4e8h8w1zyX0WkjSWpwWE*q`TBxlqB%9yoTLl& z!I>Ga4g7fQC@K{U1j1pc%E7^K2#~692t=*{UK^h%y~4tOR&?gO7^yojQkP<+K8BHM z!$^(a($9BbI=7wm)V{t4YsYst_G6G(wjAwKNzwed&O`ee(C)_0 z9T#c`M*ZdJ@S?i9AI48DgB6Yp`Qg3&$x|0EK)7|vg2hu4Je|HZz%Ht9oq9PaC8hY% z#rHmp+?&C_L$^AksA$0oLPu={C2%8r9G!?zZ3F0O>16DeF?lI{whixf z;=Nw7S@o#L)Sm zpo=xc;!}e`ry4jHG$!hGYCqTG#XyAX0aPiV>_q!);hRErp#p9XH(tC4CW2~E=exEDp;yJ= zKh5buHWdYtBfAZd&G&JS1a_Q3N~OEU28Yz1P(i3Je3Q5jzt%V4(|vpcj;rjP8;%P3 zkm5don4j`pjLnwK4$yXRF3JoRRyr-Pf|c1 zYc32)^$J_CB^b=3$V@zNlNn=2nJ!I(_b4TG%&1sXdWKDbaNzTrs3#U?U|H=Sb<^s6 z2sA%uhAi$zvO*~2)FfqRo0|5t0oEFFa7WgWVyEpf0Fmw3wnPN;liZ}j3i%6ddzwty z*-7#j*d%HpMRaeW{jo}Y5I6ZZxOGP)6(9QMwIOoxXa4f7U%cY3h|etk_Vf^Mp0Q-f zZIhC_zNFg8dp(Gi!CF;2ogqH`_Q-ykyRH!Z-|mPISU(=W^&39q*^AwGec6>fel}bi zGtAOPcrd^`PplHpvG8-MQ1CoRSNq(b))!8UcP0pGkgO%b#2lB|+V-8h%iRU)2mse|r3!GoEU6p6bNM9c`_d8EGjn6k<^zuCq5(iFCrwr@7D>m&y&acBOi- zmv#BVm4KrsN*Wyi0z}GK`S~XfN>=-5{ z;**g{iCqA>c6N3TNT7wS1-XVIv3-~s9?Xp2VP^aWGvh(b4F0mq%tSK=?tJzC?YK|> z@60kRc~(2@%;1QMW&it8E?)b;onGu?i4O)d@*yKI!sGti1AM+R!2fTimOxVrs-YX# z-}XNr^Z&DH#U_ECG98$Inry}4bQG@qPa}WCuZ{fwJip?8Wq!etD^HNul3PFpI^z+#P2A7NH4)(x8pLg*5@EGUifj+-RCj!3!XCKcv z1E$1?;fWqEW>05#fQJVKAAxFB$&8PrAzB5 z!y>w+Y~*L~uhx8YZc14`yfDX^SM5I?H*?{gGhijlX}Y+7mcO$E+3ZLgLUCS=v-nG?6dPm;wj^5N7 z3^sQIKGRvh-iDy!6qZ9FL700)0TpkE(?jzL!_rKNBU4k+JwIfjY&ESps#JFy>gp=g z9#1eme|Z6HC`Hfy8IG0)asPh*m!CC%rk*nc0K;#Q5!XcS8LkOkP>TRm$7!cCuu>H~9|ZXk&|^q2b-+9`2(0f| z$FVbjs8m$REj1b~H3}^?0>3xJXvoi@3SG;P5B63#nTyo`b7RNN0q?ioY2{+q@eO5V z(_w71vr-mRwbw1gl{Mnya5`e@>Fb!?hBg8eFl>j@oWk39EA56ThJTNoR{YN$ygVN7tQR?aIw1LXy)=BgpX+)_m(QOJj96oFLZD%7|N zUfzcCw>umzIe(jSgd9L8EBP0{zAsPvW;$rsi}t$Qpr$-;5br%2_Zzz2RM4 z5q8|K+JaFFh=Zi92I`>HZm-Ihg|?kjN@WgltG+ngD~eu_HNv;0u~_8vInXm%9Gu1L zU{r271BCT=;j%&t3OnK)4a6%HmLp6mjxQ z%(yyCp*El)invmQR5P%KTFf!TMrk1ZxA(PMd4q#ZfsAxAo0Ao~n9af0&>OWwZ_slW zgVZGWNF9hm0Eyts3Ja&>Wf{Hs(_48sspf<#L4Z^fs^}&N$}^83y>odt0#9&rAqPt6 zVN{a@&kVvzjRuu|P*4NuqhQj)y&%MCq<0u_US22D==$7w5(Re<@pG4puX`M)I^`cmp`j1#8{dkf}trED1 zw)x@Z(p=_K67m65Z3H-Ro2&p{z+*i;4`-PVbGp?r%xuo#V7i*k5Ht%H&d-r#rjHl{ zVHH1b!7qEgA%pb7FrV*m2d5c08|Xax&EfCAJuJaxz0NAi^b#W(K7TG zD@rfO*EGVSAby}>Whp}(q8B1hdeGtc!%d+>G;5qgZv$!}Ao^a)`Dtc2wR<;j-n)GB z=H&=JwVXVO|C2-ouJL&A?-|O3B(zeZD2hN@AbYpLj?H1rX1x#`hI#JD;G%Ct26lw7Jb zOSK6xb8F~;!_I1)ATzEg;6T=d!ad0117=_sD%CS9FF3?tF4l5TBSFAF%b<)&HIQl%Y(2e6EaO2vjJF4_cK>rf=|qBa#8I~qG)5whxSO+(GBD$gy? zg$W?|7PjU9+17#K(1`n5*$q~UrtB5PUY}0qLnu-25QwT$j70a~$-JN=VsN!uTy1ni zgC)@5@%HX^u{RyOMHE3o4PcZgX-igS7>YBd*sKWtmNR%_9ek6QQm~sa1CVSCoGUg; z*B0#Mq87HGUDjO=NjG9vvcR3ojpB!cH-z! zi5-W=rcO3_x;iDD$+UX)%GD;*%GL6r?P89pHRx;B8**^y95~fiV#PcKht7h-J2z~R zo<`+M9}^51!!BxHx?x2o3VxIW!g$8%$YXfuJTh?}#KJ=?Hqkq~a3(!HT|1TYNF95q zV{&Jg$0X@in{g&4lX>;fc?4TcIldf&zFE3HSU6bDy18ID3%boaSRLFIwn>GMyoG?v zc*5&B6Q*Y)?pI4 zzka!5i;{P}7%g@s&XhdI$vD%wvcnRYr0`|P8Gv{!$3j|R&A2sOF1TQlB2&B^YTpV$ z-sTu_@;K6yj-T`a!R0&Iws-H|Z%$Gblk?KekS0gik5GamxHNei0e{Yq>kak}r-912263qltmk%fk2Oq31j+*F zkWEzq_2(!GCx`wX+5=tv7#zx-TppK?Gjt;NWf8aaHcHR`9@yG#+-+O)aTW>TL@8cW zQ!H3ST7jUBFpqFb8$lnq1 z9T9zlJsDvi$h13xIP%rOkN&=EvkS}`XTIKl_}H-%-WW66 z=1ADF=i~1n@MEun-+vsY!M7-=^k<~%%2qRbR${1gyBR*~7nqc=YW0d+ZzwLkXStw` zPD&p$ar)e%sS`2rwm6cIEO0e6iji#S2|2l0GhpoAb>GUBD+{Gt(2onzpI4xJXd8<_ zkJ8H&hce^U^%1w$jR>DBv@XTct4TfH-oYq+l&FJ;Pz_f6UQgE#Cx7sODGG`RC2+7n zLvoM<<{9Ti+%O5dX>oMcfE6;js-dXYlK*Ah}OCI8zDK zU=!N{71;(JLl|>XMTH8?*&H|;p$xhm6%ol``S<+$h#F%x({VjD;q=fKz(=ED?DioA z$J${Cw!?)Mw5vK`<`6Y@2e$RHT!wsxOpZg!p^}Nu{Kz|oapxV9$v9?; ziORUE!hYOV{^FQy{dCUE>*ZrwN%w~;?Z0+d6iU}Chs}c}mSt!DJme&_*l~0kZ+W3^ z&gifLt*u5wS$xd`d2{i}!0(e~5TbHy20XmS}ci3j2ID0ziO12G*K zHJTwR0+Ha!CL#s@KOc41bVv`m)qio+dgPeOZT|mvRCW;o!Y_|%#1E`Ygk|#z<@R3|7rdU|AGQ#in958yAY0ChnKZ zj}+RY*UP16#U5q|`A~Ay?*L-`*=Q4pJq%=7;U+yRk6*H@k}nzs7q@&{9!6dk*Z2pR zub!5nhUaXn@?lYz!?Z=S*YFv|X!k6+&9fqHUyc?=b=n>uY*KWASrpA^dHqnk6OU~= z=FCtFkTn2;18k($%R&bcOfJWL8Gg=$A=cnf5tp}v&6C((X*LE_2Gm0kYJtoHAMQom zmqSIE9jfBGy5bF#mp}&WO0EXM>0y@XU)KAZp49B!x*UIJL)@7D~9W+pAFFlos9cd z`LT8AwFJQz=U{Q`a1Q8`7LTWe9;IiM(MVaYImu%0u$jZ%{e= zEF=gAYqf&QNFU<3h{Jhbj0mGXRMFl4+b@bk2raS+Zo&CcX2}Gcp9Mz|S3dOM@>@%$B^$^u^gP?3 zd7!j>_3wTM%*i?!h7Ib%$&+tReZUIO&m?K&P&#c%$@tS)77XRter?Q|q;Kw*_>G&u z3AYMvgXJig>$Df4%SK`rPDJ}&i)AF$#2}0rQXz!8ci(=siWCaT)RA8AsS|MR2!dXd z0$5bA_%4h<;6jEzZaoM2(Y{ZX}^Nu)V=8I93KL{I8 zL}n`*Gm=;GINGuXZFx7^@+!3DRcK2qdu_y6Q}WewZkks(IeTQVciGB&=H<^>h`N4v zO-C{)`Q)C`ES>;eG+}Wb+P5xx$&zby?K^kwY}ZX%BBj*SeietSS5lTsiZ49{C)@{= zivId`kT%FfeWGJ~Gfu8~L5`E3xB$JR1bWs;F&lk-XO6YB9{6ib&EF0*O~=~d4m9^8 zT9@=})mt^MJWt$D*=im9YS708+7>43x>4B$JR#LDv2nu8P|)Z?EF{LagO!TgdDXbF{>6#-m-hJu55g zcImfB2=qe5eG-Z1>_zZN*{2b!(RwJ`WAdc)f`M7)cZ7%O6wWFsa@-_57w{X{@VJG& zfp5`9n2Z;gOWt0P-k*4=^@tnm!)`BX*PZtEc)n|EYdefIl|wD{yWXy?eXqXpz=2O2 zFS`Wh$(YNom^5k975GwSR@UVi8RN!YlAb;$BQqldud}iy=Hy&?`4rpiqS>}7xmV^~ znQJtUNgtDB1ecpOMtWbF`)e>B-n?i$WLaoDH0lely1Hc9ZyvaJamlnxAo|w>-}9_` zRoO$2KMEPV9u);I-et-!cgbTF?9bA~ojblfPo-&7$BrF)`Pj6WE+B{YB0#?94}bXE zcd*RQsS(W|rqiAM3`40JahKO1=`z6Oux>tN+j5xsrC6Af4K)|fO65{-AvkvkRC5JH z2Xew73FR-8{Nf8m|KST0Q6$P_wNBDuN!)c3OJwkP2@{aRScI87nTBO8driL*@%Xv( z;ZC@xvX^Ovr@T20orlim+{K#H>PFrJSynG17;fFu_ z2wz2{I0 zDHN7Jr9{{M{yroyp6Qj|PfUO`;Iw2(LQ!3Ks!7o606St(KYB#&;=$`@h(pze7wu$C z5;p^RZZ3yhpcSHAJw7-0dR$8txD^_4B{jHT0qjSU)>t-Kzs#tgDye!VP3m!|jh*S~ zxkiV&+nAni%#pap#_Pf1u?w)L2E=?B6o?V|p>^7h$}QcM9o<_bmC(>2IMf~Ojg9UO zhq}F^v9ZG~-GmM$1~e=vu;rqI$)d=_GkR^qBfZWIXG(vJ45s}tPr(&lf-5B5Pgh9Xv*k0RD7FQvq8y513Z5-S zp69cUD9GMGPzWHYMFJAgq0)gQ(B%L{0@DjYRLK>F^zS&F*#w+fD$dM|GaHUGW2uF*d{T}J!%Y{wH-N%t-v^;!u z=zB@dTo4{ZJ(S00%C4eqNOxC$@h{vpi-!W}i*t=wGLAsvF$F?en46uztwnxyn4N+1 z3mc%0)tKRQ2@QCxM$!<$x+>W#sGP$+lT6DsEulZu0nkD66D~@2pT029&+aTCC0J;1h0cFkM;v&2H*E@>u)LOZg~ytuXFIjBPgbI>hz1kKV+ zz?dvBb{6BY6pu&;)b_i3`F>KL*?}B$6;>JF9UHYMcXEiR3Y$dn>GB7x0Ym z6v&M4xR=lO;(doW*AalBeXX=F{V97=4uUrFt3!K&R-83U6?q|Bs0K!-{HN>QhLu|a$3}z%U-|7ce218-N8>vd zTwff%8M|WTvIV8(Ik~eYO~_hOY?WdKQ(CuI5PCgnW=;B}G(@Kt!w@F}5eiGa(N(#5 zJwT2Guv;y~LdOVz^mYq)(%RlC&4C=ug?KB0|A2glg?L6fjFN#e!+Hq>Sa2m0_!OdE zrxzu7=7FqmK*&4cn&u_qQ-LE_i@RVD=9C0x9g^NpF1#ePa=wdLf3Ph?o0NoaqB)M?c{e^G@p=o=5z51l zgn!T3?Kh#s#>05E7!rei=eKxH+cHx5Ad(;{QBJTX>6*5d129FK+d9Dc<#D&Qft02D z_>s1+5bZs1So!ea;qSVB_Td-!@RLK*qfj{WaE3%>xC+O&!BJ5;)zJol$ zk0C$)ir;Moimrj}miU&hx}B;~6E2H)syf=f?QyE2ER)imLSLu+fhFs5a7#7Y5*o z&L1VsM#HG$MNyk<;RG;Ve+HdxU^5BN@*lEq!#!bcFy(y7z!y?NYwK~)G8>K21IJohKY8cXO`A5o^7eiS z93vt}Y%1qf@L5$R1ZBVV8Hz6}0LuLzmO1p9%M0Iw~1RI?B)#$=) zY@2q$Of2wX-h7dba^ie9!aW^oL!BO zL@{J@uKY<6@}l#x5}$;2p$)$!4bRdCAWP2+z`VlirZ*fv((5g>4b{eZ8565rUQRVdGNQ6BW1v`Df z1vd)3?g&{p*(rkXIFP(1@gat?)I{1FrrdI_VmL_4arT2Oh{6#9f+pt&9fm=Qu;2ya z6ovyJr(u9 zEUb%Tub6eyBkf=Qc}*3%O*BOk`Xf`@>{ZfwzeU3Z|{iqIK~ zO3HxyDifVG7tGIOXec46bae9s`CUat(mfThz8T*o1$-k8BkBbPs^Wx1bz2(>4F-X( z{hSG-;7%B0>F##jAvZKZvO%@Ux-i3)?mCT$O{-lrJvL9IOHW{*X6*Y@XUSa!i;~-H z5r;+dz(Ai5_|71ON&rA#(C7>TN*nQkvwc23=yIrep7opyIko;itaYu8 z09;YfM5Tfu|*b!r6(Eg*){#2_;)DkNYsUJ4h z%3c;8yMFTZNRoKUp+0-{WyHUSBIo(MAg(mm`f> zV*1t6T#Q%B%Q3)tbQOnm5Ztc9^Rb*o-o}EGgm>t9ft~B~0}!p#M+p*`o?z4kP&FE6 zs6_SnXdKb-;;_3cRcB%BEWo)>#kuF;+$}hFwwi!h_Q>|6cJ*CI87W(aceluEI$GDg zs#!A+vtN(?WCN%f##llc&8XIWY44)GfdlW$mfbQbsqX(sNJb#PB) zaP-KpcH(^+2z%vMosl8ZDZko_V&R*?>5>d*J?c4s+i~(73u%o$uPVe4Lr?`#{0egv z`Ik8CDF@RznbyGuT^tv{+bTRo#Tf@CA$%kbfpGN!%wQ3}$yF$Ac`e#Cz)tQ z^O1*Y9Ax2@a${!7Q*I^F=86D(fia=4Aa>q!C*dG5z)d<%Dgk(hgdB-B^8LK491>o& z{qp-%$xN9udoWk{4Fb^E80VVs=}Y3=93ns>()%;PHS?3d3+*2yE zAt6(82&YeYeX&mCIFreSkz-5tc@Mo$GRNcj?nf8Jv~LG$=LQ+kegpUy7lV}P0a(pc zPo9#lZVo@JdU4aH7q5rKeluQAM_fD;pZ^BiUHEx_K=f;!@dIsbM=^w2-G0$L9bVmkLKk0Y)SQVwwgVcale7+BDsI=Hl>QvZ6TM#z+)2QSz=_Adc zJ2jg|CMJ%|nLO#zH33`jvj{YCPcq&xDY37o6A;>$8 zA-|uz>H~1)XyCgM5mo2Iu0Q=G-S0)~7NK>mkr_&LdTL;&T3OZbrT1f9b??fha~D1M z@WT%-nwxvWbXcauz$9tsPR$^hmL~CT0lu(cus6t}rTEzNw$p>bnwrlKXJ(_tv)K7B zYNXp_xlRj_95nTXY&Hz9A}l^&1j|D(7p`&gLfwOf7O7+_o|T7Js|c`b4L^h$$0RIq z4WgiR8awyz&&|&eb8`!d>JPxl{DYvqG*?=Rb1y`2mxdDm^ux4*W)QMnL;-7!HkzFaim|aNrbLZK;Iz_ORj&Xws}7!M z$`}@Qs>HE}eGv0Z9l-Do2A?uu#Q%>YwpaOw=4HbV;1I#Ql?aK;ThR7oq36Q?NV<~bp|b4z z1E%+HWW#5*{B9=NcRH4L%i%!)JfZ?K4^x@~O6}53N}J)H989MpgY_KRhFVfliRaKt zUF4($;w*+A&ii>yD9DDz4IibIB;M~oOVr~~2Tov$BS#3M!-8RONd)jcTInyw)sj6? zjH{(J2*u7hi$&&+iJF>QTwG$EhIPVp`LLVVhUk^^bMfxYk#`kt%ndx=l?d;ov_aGa zM@;Zqw82%PK&Y0eEF4vSEtSdP8-=T+7^CYtsKXMBIt8dlrejQa1jgF60JxAdBM$Dui041z`8QG-;v#4$S^E8Y0}c(buK&=$ z7d}UF9}l7F7)@bqz!!Z{u^K@iCnTn!@`{+K^T)*LL_?yOJT?Xr6a|c(+#p;mV^KxD zS9yifHFza=!jl}$P0(pWqf}h<90v(SV)^7ZqIC#W2unqe=+Pq}pnl*16TtZ+EEzgo zcJ`&QS~Ryyz3g^adL{=;UJFLMJaQAowAI$Wv!`Ycv`Sr5^>(z8qzTq+MgDI)6x-Sx ze`*)#3m?cT6}k7bRzPJ`q*6lRr;Z+%kqYMwDt<*vE)B1dalTKopi50KNN?cD2(p&u z3~57}Gvo`G=gcZZc4+C`u9HoQ(woc5mXuG2GWZ+DZ@Z*Anw@>r5%X3pmK5*p(vTea zvJP_wV}L-~p_YiXkOEmX3Dgxdj}pPqP0r{;p%%%G;IUKhj{%M%oFmd zr{M|-c}KWELf+XxDUlqWjMeEr-Y^fDm7Ad>^11e~e^9ie>>B5R&z zKqf>|0Ca{^w*}FaR!Pujq30by^Z=>}h=PH6k975@AA$*2`>t4$(*>eGoitE?}d_+26 z$dx5dmDPke+lAzE2{RJx2O6Kj-~*wPOJT;4RQm32{kq5tMW^0?;})Wo2mv@A$0aXI z+;HUa<4?YmpMZL!wOgNPG#T_iwEeXJGys>$#7S9j(yf?*rQ`E?F$_o?sUId;2UT9taB+dzDJ2sT6*^+%$e(k#l# zhDH1Qb(J@`;q5>%H7=lVwChTJBZP* zxLC$dM;-~aZ<8f1MK@ce7h++EhEO@4ipTT)hGZ8*CFjgc8fG5tSV5c*1 zj+aNyG5+7ru@IiT#?UTiLaeYD*$1Z3=QxKgc77FGb@AyIavRxdJ|o=5XK-a~3mzG< zDFSnCr5nCO=Q-o2^Tg}&ayfMakN6buZ@F2_W*%&p@=bcd#dUNzBDcu-7Bgk+U52qo zvYb|n`6@o#9bC_E5m5XTW|eR#2JV)bvX6g_EGDSt^KxDSXGwUuW4d}zcuimqb5*bx zQCG$>0|U|~D^cJexDLN74>6mKst%jqpDzOj>Q%P@L2|u(@~v1M5!)?!HTJLujKT)T zlWZX{W{w$Hn(U0&xYe9{z{NQ?TbZ4^*J-20VAzQhdYs$(F|Ana+K+d>QC+>=h1Pjo zdO4r@!rfs*yh&~Y?sOZ^w~c;de4!tDTck~;9FY$ zVnb_;B&&s-HG-hUUVvpDhCERroE2Vr$zvJwE-6JKJeEE7+ zRaM&+i>N^4QP!>2tIW1JWix;*P>h(D*puqA{G50dv$ARjUs3|%>sRHV-F~)SH6`7< zXF5Uwv^28{YwPN|)J}D@#+%Nge%)ZIWNc~qa0kk2{QbCJNU`0%DK!qAwl&W z;{^ZLfYLrLH|nOpOWTcpfA7hzuC9}Pd{Cd1l$@LZV?uE1R7eL4p~?pG_@(V>v9ZGu zQMV-OG@(Avcc2;R?LE&yE^%B)gQ^Nh+7qPhvK{vr+L!!-6gef{LE`EWOeWucL@(uV zM&hRZKGXhFVOo`Oz0H4lYQwgrw)4&ZAjXp+ch23qUHb>GFROm$54+Lc@%mGs6*#3I zk5KXWVDPNhuOFG<6p|;~Y>$$!leSH&g?j%7(?L{0UcFNF7PxoZL5VB9qYx64s*4b> z!#4;IY{9DV_L3Q6$dNEQulUYWC!h%rl$4l*F1t>bWJ=Pp zJ~Vb8nMjYTw|)~8#+63;CgHxkv16yA!)z%+@WkJ@ZF~KLLx&nR(LjGUnjb{)b&#h- zdQ^`Ihv4RMCa7T=8PQ`SV)#)1Dg3M#7>b|^(NzcIDyg)2xN>|5G;3;A==`~U(6k{2 z!-F`0Iu5?v?v9S{e&`GF(McA|=(yNe0U6B^7M@4Z08*qEDRXif&5}SqvL)w$2jtzB ze_a8dh1FMi+P3B)q?McMkJWUwz55!?l2_hpIUP5=O~h(fY^y;$w1%ckbMsb0`OBRX zUaqS43e(6_D0iN)%xb+R*_jxn7rfsg>mulGE-AT0I1YR4I4b2tW=|LUo9>Cy%&+Mf ztr-Y%I{_5O>=B0L6obmd5t%)knKZntBtJ<3?n*Cixlc}^^OPBkDdP%F-u z;JMgXEknKYv*6K-mDUY)A4*GvH>H8JC}nioXb|u-6qxKh)_wBiIS%%M3eyU;CwLCK zizzL%w8VA!}F z`0UhSb%`?r_Cg0F&^c_Q>Jh#Ux9l@jWO<{POhhRA6(OL(M?Y9(LfOdO72c0x(!Ug3@HuP`>PN_!^b- zwah}v5V)QpUq?rB1g_#=fx1WA`sO{1l#yh#QUK>>J2H6|3QU7R0 zf7AB$8(-g@v)}i=O^yw(3O@wC3qf$Ie)#GepA=)aKk-EF(pjTb{u3Yl`Kh_^eBBwI z0e{$7dAk&b>YRwjtO$$chYe3|KhWhB$L@M6`+7+3#fxA}+A(XfsUEp=Mz*wPl1>N- znVP=#fA0L-C;!#ibM`!r27YOTT0EZao)c{c_O|wqIH}8`>=cUylwpDpZMrN+T8pXi zu9399AEY^I77U#82eh%^t>Yc2UXhfXlpwMJA5s(gbRelb)qUzzw`?PT7sa(l9xvo? zD93oY4&%j!@iGQ;BKfJO^7G*XJmUUQ3!?#H%_EJqixb+hOwC;Z+hj_MT7^)WLvt?aH7?oVZ1e;gtkebFt+iV8Dn`LOi#c;w=AfkdV& z$oTm5d_aCW!dLS3tkA|jQ3^G%Px)D_J}-P_r~^QM2RIQ2~cQ7`_sfvjWHc<>8 zS{K`m{KrAW)@mYJtA4Uuz&AZrI-nz_&ht{Vlb z`J#4VCi@VA^+VU2V^Alr2TFnf8qgPb0;ZynFe2l!aS5u>K-brYzdbo1F-j0$OE6u| z3Hu)4XlNVM%0;FYinif=1A}mV1%kXP>=>@%gFXaZTaWpInq&;t6pi$zVvUue7swi$ zgkH!)FOW5sx_NVX`AIclD^t}czt|02Og~m28nqfhxA0(qsGt!;={)h{0IxTVO0y)#^8W4<@GlBr#B>T^ z+X)OJL@S~Ki}C{HtpK7y)Bs_quropCl4df!aPTAq#Hk?Dj7T5qfhFjHo6!U5=mCl& zk>8EEWr$#uyQha{Vo#6qnse7vOpSbBwA~!8C%+=BVf4c)&}c6UFQe7d*G;{e@#)SX z%j`APQcW+w&Wpp#R6FO)*(nV}HT6}?@-YrFtY!3kHWq4#a4Q?@LvR#ef8dta*pZRp zAkyV6BF?+L%h&GlV_4LG{cu`juhV#HV)GyVbSbTDFQi_3vdQN;V*W=xal>!Qy4Tkw(n52c< zef%^=u--IM*Wa&`w#ssYMr;w|e^b>34ToVr^OHutrCGD3y zi#LQ9@}A~qd1%@J+Cr%Zd1}Iu{iaX6WjYq?^3WKDCXXE>`u^1m$XrQ%eTkHmk=gk& zLCbddsnv3CBjanyge$K@}N>ZXvjudbU(-T%F9j8=`2)+ z5=~=TjLNT^fTtaP?s=B>oWfIlH_m1?&SpN&CLd#B2F{83@-auohJUZy`&}x$GKGnU z_pQxC{r5z5fAI9@yI&$sDn+G~V=3@@16qGJ zUb`aOZ&9c;lbr{n=&P{U0P{fRCl*SIdGV@hQEt-?Dw(8ISAIa%)b!<#PWAcYO}V4A zPHyycN!Q)|@rLbxY3eskT|#xY_Ck{Y?a$qNFD+!Z;dc)K|GiUPKGk&cO?w`Mx%F4gK2Hb{>&IErq=fzZLA!U&RRqt$6H6a)O#X8}m@#mDfDv%eAp9(>kBV^1 zfuKo-lh%NlkQKhR0;F;C<>4ZaOpLGwWwnc*ua!sZE|8f~MAHo`7xZS*O9_5IaU5dV z9j(I?4MULd+&RDW(vxiPnU~~w%~2j(2v_nKwA~lD64{fyFkd!Po>8rwp9RHATsaS~ zynmrp)(p{)EuLyJty*>a;sw@RIa(C`o03Te1IScMX6M@S$R~q+^2}z!nlG>|$cHar zCwp4;K*@v&c|>(6JoG^M0#s9^5W!o3rK~7~9afF)S%fs#aw}I8xT_VOgH8Oy#l<$c zDgTH`^$fQf-=Z-0oA}mmfA%eayQY~;0wg(nA7d;Q%UH7+#55kY*_@77>G0KIMF^MO zXmFp8hN-E1HV&VSOHNkdGga~x$(P{63{+YIGg3G)F%?V4)SrDZJvmt;@2c!670%=U z{*Zmibgw+a5)rx#f}8H;Co1Aep|>yES$P9{ar6`10`xag&lUqkQy?qcDG*0@L8Yuf z+;AoCm*Z6#o-IdM{0YuwUjf(4YCJE4qW}3`6eA-?VG#ha#FuD*i+vOV5fX@%7D;U&;JXxS+iM?Ta&%g2WeWX9g{jpQN-};aD>{O-bCd}o_ zA;)H5E|0}r9#^__R{xQslc#3;V__)7W?9{xfuiM|9sUDi+Q<7QPndv!ZceCYj)WMF zGc6maiL?oYje8GUvJV|ZZ32J5Z`Jgc6c^>R@0(@OA8Is9+q`}-SObpU zhSH$*pvr34SXotDv)OLnyt!)Q=IScxZ5za(3rY#KlKDBymP2Z**K~LdM!z*Yi+^VO z_L&&BX&Ap$yDSa!Lz$gUT?*oY$&d6y9F&!U* zIMW6CLG&8v*GO;6GMUHKt8w)gb0@i64z`l4nP$4niQFj`4RHzRAo(udA7;xMaEm)C zXfjjUd!@t97Lv9Br<9Y#zw%wu7*RNuHllG9jj@rnLqAwqm7V31>Od7FdhjC}cHx1v z$1n8_X*xUK3=|Z0Tg=RnN(qr;4QV&BstK&W$%^+13wQ03qKgYh(JV?WD4GY= zAsgjv%dp256wjYoSUB^>qQaX>N^Y7D6gVu+O|t3!9A8{K!y)WPsC>UeomJe{mXU!n zQ-(_y%|cUD0ngrtY9rXdF8?uJ+ck?a0JxgJrT|*%Gej0>`;1ldYv$*WkA=R2b)-rs zs#OjRL`&d20G&o&EqcE1hZ7R(3Jlu*?{!$$0Ao&=LnLCz%y4i>lK&C+oP+V}G5KL$ ztrO7th{d9$kJsySsH609;_aJCuhKlHih|dojfYl{++~MufPx&$RbhLOLyC-S3AmB1 zYQ^g+6*6YG*q}lbG~#l8OS4}}X(%jgF!${TRhu~S|y5oP4@;xV=i!ebtgZxub*WF9ZH#{7?u1&$INW z8XFr=0nyhX_f7}Qm3JdvVFFuif!e2S3rrf~WTlN#BHu$})AkptNjLg}x zfBY&W7JeS7Nbxyz#8u%^ZmDht(nEJ6?NMaMSgr6V@;D#OhbeN56QvZhUv9H$XtQaB z4Go3vMzq@p$En@45|?}RY#=M^+4tO)An>{pgevuTeKkhb+k$4LhW$^C_ac-~%AD)1R_m-e z^KW0Wgeb}v6imy_y=H2`Y^+IVgEWm>Z;!sH`TP#O|7T4o??JMQDnnFY?A?R0M=0bg z(2AolOzl3Q=K zO4+c(h`7JM=hP3L?@t{2*O9|twSCieu=Ow-{1GI5?ZERL`@sv;Zclg6h`5Pmj0we< zOaDLa-Uhy@`t1Kd=Oj(jHk438DW%i^1q)WJSh2bap%f@su_7um#HxtMsxofo7(-2Z z0u)6>>=<*^p;l$CW1XU7$dtLxb=(~?hgh*<#ex-TDW#OOO_SgIIwyttwC(=g_x*dl z{{MUl&C^NF`956V>)~^KuJCPxOS~n&+wr^K@1g|o&R2d@^_y3A^601Qy`nZ>0>Xv=)&~Nr%axdmv>o~ zVmmJ@D`Q;NCF$v?m1#p0`T_Gs+N@s2;fo>$1>fb1TSO>kU-@XEj%b;Bn& z)PT8*2S>6`W2ktN#H(q%JBH5)yZ?ATpTn^;I7vZ2365IYb>Tw&HSfiEPC|qoxRKJs zZ+CZfcH`;`YMQ*$gC5aCk!;x%s8+=g*ll?D)iNdg@CEYBZc{8#xN=GTwcBIkB%MDF zgVwMCfG0(vt`kDhCaHrOAyA-r zPtn_5k!~GCxLey(8#XITM+k zawe1Eyed@_@4+{zzSbySroGC(FP+VdP);a|-K&8J6e}xK9NG}7__xj`Gd&}9Y-ZMk zoGi{pW1#)#PRJbTUM`q1oY5H0NN{^_78$s`n8EH*CT=gzsw*~M|4^u_=q98tn4Uo_ z-k_dPQ{(m4S5>|Cdgbc`V7J$9-TG25c#mM%2UK|1XCqq-UKztI$~UTl2=(_B*lXz^ zNljyEp}xsLghnL{8k&+o=807!S|Cde%nH71(ZMiv+562P ziJ=SVPx$(E2W^g?VDMzi;ZI<+z-?n+tyovH*sLWnv265s9r-^Jmz3CRR^&Qlen<|y z(Dpt-o}v_**sd;Ekw?z>8ghaD0FEnd*aEU)djiePxpEERTcaP}@Yu$E`)a%O0))9M z^pTXfBBYsxVla_crX!~F)tbKwP@uovo|=|+_Hr`qT#$fDOim6{k?C_W03bK(PiPbZ z@Wn#S0Wrr(#JPoQ6n?O?R}$-wkw^Pnjh*|HCYx ztu4_4!N8jJ#yY!--xe+N!pN)^roTC4h<`@@r?d|VoXu0$sAcL(qfLhnT(YC~eFBs_CTg=HVhR$nV)Jsa?8tE7DS2Uw=c_ zsuXJGrAQkb{LirQ{63;S}9&z7GC6ZY`~pO)~r-0#$vA`rtwoVcdL< zV8F})mNbf>{*)=TB&sZtfNSXq;f)wePpJ1jC{Qn2%pz)sUQaBba78ZB{AZN)uEn!v z<`6ZaK#AO54;^5f^cd{cKQ$bfb`5fL#exwIZR<6 zRrgOuOs>>Nqg!T-ce#p+ZuDdXnO9C}fl&@wkbw>hG|NGU{`2kQLo1 z#zDbJdpuHX0H4s!8dpV(k3{{iV|=DEK4TakZP}7#xrWv3wg2eeyJurBTyNkl7Go#8 zg%{iOaO=^F^B4(A&Pj7Z0 zcmwPKvJET@%Z~xyf<@7KrvAet?LK?j*s<}=-2~oBY)qk1>G($%8cz6kl%rtJW7Sbu zd}8wrKm&Q7(fAo?k5WU{iVEDSMn!_IaY~T<7c<52Bb3QnF1lJT5l684Oj>*Ei4)Do z8b5l!?(<`(x&kdt2M?b-b?oHvmQ&3~kJKHafO+$=(|lU9Mq5gvP6xU>JK#m+u7FIW z#5i)R)aSq$;M?%-vtB41h-E;OmGLT(0Vn&hWjEVAH8tw^tCt(WfvC*PF&P;ieOPt1?8$-p>gwHiR{m67 zeTt{b(a2M{ee+llD-nG~g#Bk=C*`Q?A1$Ecix`e+*g?yIxZI4unZo!13{XoSnqoZ9 zftYI;Aa+ha4p+!PWDrSaUwR5dn#}wcBZj6Vvdi;VB9rNrFTMNZSKrm2Ng-J!7Q2Wg zA(R+o-%?@~>&SfSCovFejK@|310&AQWm$|Q4)dXI_98YH`h~*wuhBGd0N z%>Q&pMcFh$4!sf7+rrDNn>VxMrm~EvK5VrA=vVer(P3Y$eCikFRj)jwyFKw=%HvKs zm%%+a*h*HGnZWIfiUka5WD8sSW($u%G>maKpY}h<68UEd-sGBKYf3XoEKCU^gD#{* zhSQM5??_6w@%+D-c`SAFq)DE*nR@J1v^UpHinR|ImO9c2t7?ob0hT-s+q$2Pnq?2~}lsm=)c zHzT&|ADGFt%;cX&W^w`YK&XMG`K${rPjEiqVgFsaAb;%Gv6E4xpN!5Mhc0nh;eQCt z-%IqlU%Pub+c2qfxFwg68Czliwx4Djmi7$aU1Iy*cflCVGa{xNSgLy?ZL_i_>U(_i z*d_mb(%Vcj0-5xZT(8=qO4R5WpHniF-}>9MnHq3`t6##lWNxaZc2|1J^Hk(PI;R_IfihIxSQG-&hN~4E3N9 zRFIK{&$c1FRr}b&%;e0M+2-WAk!(GcOsjL**0K(~k4U9bO=@a#(ufhmN2VkVh>syn(>-ih>Tn|e1Cqq~A*27VX*vj~{a2VeRx6=x zhVA?V=FCIPnfc6_YnU@)mGmplncx{S_JYgj+^}@z%9S1@Wbu3o2$``D`TsFX@vMot zQfhu%+>aMsdm)BE(%7qtR!zp8@RB9x;w!F|(h{vLCmaLMa|e2$jM133E#%erFL9lh zlr#x7_-tPN+t5^7d{VC$)4t%quB zu^=cN1^)4$<>2QTyY70y+}T$Y7vcX|w&H3o&@Bl30{;XVIkG+}ymGg*V@G-+lLe z?}lmV11~7_C@;i(FYSf`d~lxGKbRWHCEt#CeL=U+v;XCVnwGSI){>#2=%n+~0OZcR zInQ+`)8)FP1~#V?X`k*1G-;sVbo=)DhMv^)f(fqGNr4ln9D%mF=7VfC?aCl@uer0%kN#k% zeka(rd^J~1L-5zL3%r5gGIgq7pbpi*IC`409yH!R$|t1sxfvsUpLc~;N1D)e)Wcaj zSk2I%C3F#9eZEuc>Gt+OSJ-NI*ez?~#1aVMCF#UBW2+k|lBBvb4TKb>2T4s>BJYp+3WP+kbDkkl!+JOZyI6%C`<%U2~vO|K9)f&|%#= z@ciYOL%8mOe|HFDlU#V6&kbXYIgD+xEvZwo1K@z_~IEGbU54@ZlR**F1OB>c2`=8-Q$^2u;Xul zj=!)wF7)V@B1p_7@Z;4$UQHurTgK8esbWgh?-eaD7O-$~?1y~GLQNiNR_uHsLD^~W zU`Yq=Kr%J3n1u~5$l~5_EdQ;9hqPa2wx2o^Y(9?PuC=YbgNo>YK2Lqo+Ip(Bsqsk5 znXb0B_IBQEmV(JjTWc#AdLLd1_THdQT*B&fB_~nLC(!YTB{tb*_YZ)5I7sF(#IQhT z@KM;T>zo{FwULn|KDje=ztHhG`1#N;jh$AQjjm@FOkoyC_M?kc9lK!utwlw*&MTUp zKW8!;*e_%5nKwr4sVf&01M`i`8t*%%;P1YiE}|$pX7kYjble^z6`Iod&fV9Zy#fG;a4lvkoBW(5IDN}ywjmmHJG)5_A?Qm>Nf zn~t(7j^j!-#W?*GLe^b$sjM^N(?po3c}x!%i+gdn$Wpd_%NC)0oo_66{4u&R|M6Kwr-bNOy3RA@wzH;NqPTO#B$mXbfj`F=uo>XOzMjxwdalPv2YH*x1~7ntTqj z%{|U$V%fyc`ol*DgG&1)ym+DRa=Z8M&vZK+qf!K8!DkOEHaB5NV8_%n6M7Y)TKNFF zszT@^S`BZH$f;|vBc$CJ&M{uk!E2yMd= zX=!h#D!8^!Nn5X!qWVTxaUo@8V&Ahib*)xOUw;*As0>(7pKgu}^?_4?26VMU|1{`YG z#*P_mbOc6CBJ^NZj}W^I*D3d_NFJ@KnqoL8^1vzhyuO(LTEy1-E<20FD3&0Zio)|_ zd_Q@7!-mHnSyx&*jYA7SnMg5(Ys0hk*!p^8Ye(Z>>2QDInf509+lLP~HrChG*VewZ zySCYC>w-Jz%<1OZx9jzL&WfUXB7q{Z7#3AIH)GW(UTp;RDLC!Ff}Ru6Wj4u#+iN5) zY-JG2k;Jmr%J2j|&rY*lqOtnR2fGr&>Hwz|)C%RD zqiwipkp))A0A>=o3TrK-@>kttTZ;Q7PogH!*El0fz7*~ zEq&)E+3{tan34NMARK={Ar^UMxw>J4K4eFG%W>w$C+~l1AK@+><81yrsIY3KFyG;y zMaxSyhXtJ!JjX2c`u&B-ntgPLZsX;J;C zATMk`{YQ*;fntIEMiQV)tX~{&50ign>rb9N(6J;6yvVIP&;=2mGa4Tzfw(h@(}Ap2 z)iQH+3Oi+QoaYDh*(mz#{JA$}*-u4{N*meFdgfqV?!+YPC%>)w&F`D6hZ{bobik;z zv|Vq!qn&r7etv6k==e#~E=lemV~=(^9G6``=ekSXLwb(xd$%hR#y^!F`ySmY0k-=rn_qopdoIfZdc}U5E5U+2zH7&eTVLE(3gP}r39(vs?)rtzNYwu*GUDSVP=nE(!2!I~g)t!oPb~VKbm&gy&vf4&HnB$L zbvV-H-G+J77SW12c;_={-RlYcN>PvJA`846;V&_Ws)#DOP5;%M*k|bs{Q{J|saS0j z41%K+;9#Qwf{i0+bs0ic*0za)pJh}BOAB8Bq<U&?byc^m@)=V+J$N9trD;x|*F zq7q})(GylwTdehDYd0YCH3s%v_qt@xi+hVd5(45;Cna$$mN4A#lyDDfYfTKbn^6>A zp%HG4xVFi4)Zb1p;-;C?XJlV-@%Ze)>}2Q?v3}d^IaEd)N1nhAsZ=EG-q;Q0zuB_m z_1AY)?%2VntlcH{9;NmjHjYoNdMtozZek~@Gh{*{g0p1r#zM&?Q6^;_H6^rgiJh`r zgIvz|C`~VAJ&QQ@lW*V`hD_AI2){~-TUFBBrnWMGGZl=QuyKltV9}zp3o_FdFTSD3 zYpcv-MCSB)PpOa^M^&VJNv7cDFrUd3!*!o>b?@%axQblbYC1!C4_+yh7U-B-?Y2;R zfHQQ2s9YfWz#5?)Hg%To(R+{3dy9}8S8-0`Ij8gHE))pl-S;k@JGY3!3)kl7Ts&{- z(wi>MxkM&gdxi&g40Z=cg469vzH}xz6UP3aUnEx>>=uhQKE>H^xCM*jk+S03u{b6S z40RmX8VtVhBD!i-3j-Q_%NwA6phTWm8jFI7j!$nP+j1UrN#gO7P*86Mb8sjA0Wuhw zXG^2KPyF_eyLZ3!(;3Vxi3r`tX0hBzH7O3?qpsO71xKRP$U#rOFxu&I2&ZV%fx4h8 z0jIRmWEH}?_u5^~i=hLYHW~11c55de8JTC*?=>#tx9kCwL10Ww`@&*a5Ved*FYDhQ z_{4sQT1@+*l{FM0dNphnVIeooxUG!GdldJ1!`;IZ9r4b@4mJ}>!_nd?jnF@ul2EWTg78pbZV9eaqWRO~Llsg&An^ns zsFn;0b`ks&pD?ya4QISEkKzN(5Km5;dgt1qM+>?q@GZ^z+#$2*f4K;uQ z-wA~`&%QDz=gQeP7fu0#kUs@)-=l^^5UEgVTYl?>DJkdQp?jf~vJ-T)o~cW-by9*| zfx&U>)^~!+g(wE&GY;(8irgd0mQYnyvEgUWJy%}-+%rE}zlqI!-A{%8{|-VC63>_n z468VNr)6_xh*ci0A)R-jkvDGU@7Z2CSOl+zMq4Hej%$5)=Fi`IXDVKceV^{`0NRK4fGssbyrX&IlH;?6SEFRAhxl5hk$+qyhNVqu7-?T02YnImc!zHu3}0 z0}a5;OI%F~@1P8P`f&}<-wri2C`XU`?~(#gv=117N@M6Y5T~GE8E7YA=9ea%n*YG$ zq8X*CMyoCu42b2^4EjHWu@gCL7PLx6sgRQ9q8wkM=IGC&Jt^9{;lW_166_8`M%{U= z<;3aM=0Gq^EC=jh7icr^1*d&D<+|{|`7B*$PMy?CG_|YeA(V_Lzt$P(T&;C?cR?ev zPDD665R{cRYSS?nV+^#uZ;**hV5oz^4q0+62EhhD3jj+1pvFqOhLv_REA1*qUM&65 zjJ#u3vEa*>vqxN7cwZh<*K8D+x@}_SVv10#w%;=^kG{;Aah>iPbesgiloZFYZIzXu zsFsUaU6-e~$CXgkR2a|vCJTpAWh`pT7MK&CxCUvu%-AT*S`zb7I)n;UAlef4UT%9x zI8mS7#Fu|gt~ez+qOtTZQM*4uoqoT;6;BbN_MnpF^51HVk(k206?2S*V~mrabMV8M zV}kO==&)7xhT=@G%P#TBP(NM`|0F3PHbg`X>m7C!D6h4c0nPy=k%ITMg&{An#k+K$ zVq1;<<+H>LRKx3{aHAx|sD1_rc_2)n()+$>Q3^WIkBvwe643t$6gOrar*ZVR@LdbD zqvT5nj?$W!S5RP&tem*{GiP3%b4Avr*~uscCaL3>Gz05~4PI}=Jh?0Uz1Y&t+csCe z^1G_%e_N${g~L&`hWcsVjnMiMCT)~fBQ!)kC#29i}q3>I*e{haR?gA>L2VH%t;7e*m;zK zL|h4a;CK_3HSrLN{oxV0m)YdNP7^m%D_KqEehWP$&w`TaM6;Q;oEpokP3W^?!PR2P znG7cH@OU56#*`k7rc}XqD4U0v7q1KmDMnEutgR?g4#)`zmn1VCqsX`9H04ZXFXjz{ z>_pRF(O?oN74#Un3G%kEZ%2tQKMYGA+TiL}&aV7h7_+I7l`mrk3OQlEHg^7^#X>Q9 z_xu^M!On#F(tCD;_1TIh&=;+j>Xx?MO>P4sAQa|tzW$v61U~M?2ZW$77XUz!x}_|i zk+})8*i68?90pq9>N0mzV$C1B#FO?+reQ+6$?8i_dCu znOhGa0zUAmgzS~&w_pWSD2ojl@C!~+w$%R`t9ga@yg<+fA1g_RT^}884qzoGcC?|+ zggd$*XhLbBq9$>1If-Mv%;v$>N|&EYhmez)w~;Q2(+e3umg~8IcZJc<`K*l@^m8u< zwP5u|?AS4G-0l$WUAFen0NWSGj(uSpkUsFeJ$nu%jJRA#sVC`L3f~WAus^&M`oOmO z^a12i%)Mmn{=LMkD7t#_Tzy1VR#Q{DbBIiXL}zL`mBiA|)YRnO3rDc#Zj5&P=NPda<6Cuw8vyLCXvYa2ho zs{F7HFBU%(UTyARQ|RC8=-xQw z72eTk%!8N99`<^t@qxRHjcbK`RBW?QNm9f%Yp}9Nq##Rsd)jbtjjmRG_P>(AqR%U( zaZ80}YQ7LSE?)(;B#!BLN4z825g%_)1f!SIKUQ*!9CmcN(3$SQnW$JaKr3E_)k?Q6 z;3ghe{9@f1obiD!f!-OQQZkZ6js*3HcUpWhdGuX2eRlzUCw!U`LZ8z$&ZJ2fqJoE% z_5GGFzkB%|Sgdr>@cd$OYjkT{)xi-a?|ytdy!(HlS4ghGiq2+t7p1HKmx~ac3!LY> z*xC84`|08`1iJHZsUYMQgs1C1t+Bze&Sc+@CKfZCWqo60Sy#zsEuChF%&)WX3usK* zA3$(x4<|Wo&J|?Wt>l;yVFaQ ze7`cyr<76nXpSf{h&~Jkb;||>9v8llWs=V8emX=-w#=uiC`LdjBcGC2qUHC6G7QJ%5D1#HTg;io3(RY#(qKmHecs%opsc5(X-_3Sj+d_n}%y=yN zE-boeUrrjvLGdQ+y>755j z_>fX1(!NxxjwbVsKZTJX?+@MjBqFYw#usqU0KM*eiIA$~N{*TekkG`cJ>wvS|hi?h0n{Jm#mg zTTrZ4L~VQ+k-Bj@?<_V&EdNvSFlxaN( z7@tb1nC0I#*S_ub|Ard&oDzN_E7WzerL8NZT5Xn4Q{#tq^;89HY5k%pK92elTIV-u zXBLe)W$insx{ZoGzKF_ZIj*R!Bt~()pgbizg!_E7Wh;Nr##{X;NRo z*Y|Os-E|+k>p!S_CP~dx0XfVw2tJmvgBAkuA0JoxP{r2Q_SWuwZEFRN6Rc`=%a$3; zAK8YMpm~cxyFp&3*vOMm7%xYUyFpLtE1)8C#1K|rtB6x~P zbe?1H_FfQdZ=-@1Xr(CS*wK#;9{T*)vDUT|#{ymG(}YQmcflao)fEJ<{ZYp^%aOht zJOd5q%r^}5nsYUHp6ji_6V0ebGpb~%F`j1BnJi+R)Jhn_(MK><6sPW2nz$P}y<~NI zI6^dqOpXu^{6PnnUXz}blsb0opfC0<`N68Yr;h0PNA-W|gC z{8#nyctZLee2LU3N%*<|5z!07p33742b{ zXopo%vBm3y#F6`zn}uaHsI~RPNy;<_PMv55e9~jlvJ z9$*we*DoxZKRX>+6D3RmDb6JK46KtSxY%xNZExCzqw8mwyyY)fR=xHypVF{1b%7ce z8(%JyLSRhS$m*p{Gjy6+XVc__s7mTONdzJX`H+fgGLSX|S}HRqa0sMSPF|PdL(;Mg z4Jjjh8THGn-~P|PQ@eiuf%og)|FG`h{=Iwl?5TPC&u{<5J>vWe$6a*MMHfrW!>p`} z#*7|4YNXpee1v{6vh%-q6B3yC=(WR8#)W6Zl|bCUNzJe`ttTZ}8Zs7coom1aN2=a` z^NQR;nQ7fav1BDhR-~QP*xtJ$MR&iP`6zxhslYs#`Dn|JUA*_`vSl7M0fH(Uf1;Ny zv&~<6AP2u`P{=;xg3fJSfz(9Tf?#uyk_F2-1@$4k{P$SjI=piy^pg{%X(c;%%EM{l zMcU3y&*}s1X$=kbW8-aIdpeH2WgB5{Xh^eDYQ;vZQ9^b$nw!O#OP@>J@djR-#%uE4 zxUh|?C<<;m4GJ2DJ02cZrOE%(ftn51TQF8rIzqvA(#(QEo5n{aJAW2eH=V1Kx(CC! zxNTZqY9VIMD1D6r=G{pEfA@H;6ue@WdXXi%ICA~Z{~YWw@IdOq-j6d z4-o-sv{I$3Uhgc_fZr)5WuExRB`RaK=Xqxf`EDLqeYjjDDXs^|sUk?0)WJzMn31t# zHE8roAwmq5IKsh(Pk~LG2(&gfwnlnF_@#s+N@TT0Pl&{3+`OL98$Q2BY4UosBp)+F8oj>&;{`y%u?pDscz6)ZWA3N@le4! zMca(nm!?e%WrS;x4m(1Hp9S)i81k+>*{BSR@&MPU0K_Be&MHkonegDCx zo~V+TmXl+att!6UdZ-@9L-fkqrwk)+7b1z`m*XZnopgg#$dS~!TNi~n1>&$moQ*1 zR^HO6)&mFa83}sS2fIJb$oW2)FYW#r=@Ot@5mi<9xAMC2dARDu!M6_@ym{_wA?m$B zkIpHYJHgiU_NE9ywC#xtnb~H8WX6~-kBzP<` zmJ-jFv78xBgGHk$bS&aPdF4B%5)uDK^ zmhaxZ`&mzT0i*eJpV3S(M)SuMn`l~j_o79&&-?mNE;5Glp!z(71JdTJBN$3I0k9p$ zM3u>UM7>%4v$|h%Y3FN-`g&wAy&%`^3}z#wQzkjshiay=`WQ@`F__L!i8-40PR2Za zSzVsNjI%xTpP`hLy0MbEAfjhC%ms7YHIwn9{j<@mz+cgbG|Nk98hfb0L8-GuvVKxY08HmGnRhFQhblHE+t1n4J`14 zm#9sMrA4+!w|N$f@Z6m%ZWLvm;~vk5?!B+?`oqDP5m(+dnY3ST^n)`;I1W>V!>4TZ zc|xmvO1vioz$Uac)Dk-6WrJwi`7|N4>yhr#Zn187dgtd+l81m(Ddvf%D+x;cZR^vI zc*0jvK6%7cHI+C+u{xRotcdfB%S-%oSy@@ND{o;QQrV1c{<;5sdAXBt*i6owR)F_& zI+|M*d&CZ(wlIaq8`S;#OVkUN<*eoMMl1#m5yAtc%0 zCaZw%bi3{~%lcHbdz5#*fL3!UVFXmj@%Z+dLmC#5T3@sMaWZ22Ra8{&s`-e`zv)2D zYZdyh&ff)tYPkestvx7S2Ql&bU z=C@jeUFm?n)VPAzDo4=oFV-A)b?(G6Xdw zF*)TtcZBCq`~jtmq{KnRvJ-taH}o!A-jX<1^?{SaF3q|mc;L-mF%!4_;OUVb&*V`Z z2j6&XGT8Lx7F*j&Os}Qkm6r9dD78L@a6Nje23AU-g$rXI-t+>SS@kbAQOaow1OW}N zV9fuRaWvB!uO?4^Q+QVBS)XOotBpNu$nUBvR(Zk&N%eV?F25}MGEd&rY18uo48`px z_Wj2Jd3Q}=UzLJ$)#_6B6vio?80S{?4o0wopI3GRn%?C#N9)&!!EykVy`!~>XRxXG z)Qsgbzck8;$jow;#7UorxYYwLzXv2}-TDpFV2u_ewgB#m!w*=r+T8f~|cQ3&WTWV89q=RSx zP#GI)fLFAy-s1=g^*PR|9FPOr(27NdSc2s+!VQuPH*m1A-hk1Euc!kQ_n3Xi{hUGh8UuFL!-?XXfPnoZ;k zIrPiNNMA%V-;LkWGL=|XYXd!aHn{-4+T%W15%^XL~r91A0o)J2OX+H-1xyXEI)8!LYK3PWcpEWD`M7tvJ!ka>Fgl=_q6EZR`9G&U0A8q`!r3aharAtIfKq6-T+)dV8po`yYg)KZKs`W!$SRXD#-6$-anTzuAoxGxXK!v`02NuJfAR7g#Zg&13 zcr5&-(o6(<-`CCRa$8Y874|!G7(W8*Xd);+Vlb(9Yye`dfFe30I!{=CxTAV)=HOs)Y^en5Tt zX;0{_xTl|fdgn(+(+a6X%Wy6R77Fz8U{;dnY zc7CZC8uzG=KI#d*?0EFiM_>Be0hedmv}wwwZ#X*yAh5uKf~)f<=Va%A)a2}{{)f&^ zcH>JqJ3&+T_MXJ7p^l?%H_W1d;V{b!7e(vq_jKT?Qaqt5pA+T5o?y4 zd3JpOc?2s~EM_KNHhE6o+({k}XS$0Zjk6>+s5I1lz}7OlUxUAp4qb0_=RO|o$70#e z#3YWyF7q?e%4Lr!^TnflDw-oI8q}=S!aj4ZQ`gvoc&-&kj0)>W&{t;ez;Yeq`do)p zz`e@Q@hd_#v?g3f(hAWoSJ`$RMbC$8`5UgoU=Z>fYtkP#ckYeX-MqAD`FH2fH)h5E z>ecwm42dQfLP5ApXFiWbqK)KA1|P0?^7pm--hJntci(zrdt0#az(?yr8mT z(5>3L6}!a}^gnOT2dQmZ_|;xKSibSM)irzm@|Qh3U#mRUUcdLf1AD9A`uo1#67%;* z`}ay}5gN(A(~U|-9@i0RDJ$-rH+R9(rAbL>(C1%~pFb&sZFYE4nn-7QbN&C9dsDR3 zUhTVACpLA{XVIof9qd)6Q_xP8SW(>1>eI3^FJvS{2NWi7(WRzJy*#WOysDx3@MBMb z0Yut-tN*Qiv51%hzWxyjgOC|*-=6@pY0Qot5DHZJ{a)`B;bi{g&Pjlji zfVb7w&~J2}a`^91T=tdSyNwxIjd=MPV#dpR+bEKYJ!H3QFddr|$6CI?kMK z?>ODblc3KQphmsY)!C~TW^pYUU%Qs-P&Hu!q)TWH&dgq4?ws%{>&_ZYS@8{*5?fup ztFm&}h7AOobIGbI1!=wUF|H*0|J9YqsGQ&DN<>}?AwzEXWupdSn|1w@WCiCL#%Kz0 z^I5pVUhwrd1wX@)XAX?+EGk-vex6n^H4hB)m1DA)>s5x>w+5Au|FgkoX$XzXV?6qb zePaX={pPM}jEYgTSQG%*F#zDiTE+geFc2L+iwudrhB^v>w~+>p2#cy*L*LPmYao0} zdp7hI?Jzl251}XJ8Hf}IVVsFxHrcOV12SLm!lV|&t@11;FB|=MYT^Hr3o)gY$o#7r z4UzemvqsXm5~flv>M1SN`%v8%+P7^1BJ(q?)|}Dd`q;FTZXd{Sy6r@dgXt0FVLSKP7!S?Fa;bj(ciWM z2p_S{kr>n7VIOGj0ir9iG-HDnHiPc=uWkrY=7ym6*#{)|{!2aE)injby|fj>O7Jj**&0=wf@`cyocflJG4Bc@ z*4^?@osiL@sxA%X(Uyh^U4ec30?Wl}3%HlBn2N>LJ#~emrh~KGWnY0I_GQZqL#(f5 zrv8xO*^K+PRE5bxDIF{(SFibn$#vHJqS(Q1!y3Zn{*9JZIZhLCn|#$0+f1bhEf@%4 zHk~!0Qiqt2t+u$KI&?k)!Z9qR=!m7{ADU#hC-pImz!ID@k|Nb3q&KgL^k!Dy-ZVN= zh&!dpjV;gq@wL5u82P3vDE0w z!DqYgE7t4TKBgoDuXXuVOH=wd(wBqj z%UsIF`d_9<&??|P*|3wilBe-@i}K#2JT0{Z+9_|M!RxPB*G96Q{x`on zh4W6Ohla2-O17_PkB*5G*Q{By?x`Ie)``mVzF1L3GKZW&NxW2#n==PvdCHY}SvO+v zvyxE?qZ>lWnTA$4^Q_LX1Bd;lv%27;$SQ)D;WNiH>QbNuMx%0w8VJ2s&=(zm7|>7y zA2s@N#5bIiI&Rz)a#N-K$o9zAqwV7ui!Yzq(xr?VDPvO5+4Oicd&$5Ha?6H41+k6@ryH40Dp;gp%N}Y0&v+ zR-|D18&&{yInMg3fmrJVH!}HJFYupaFV22HZu}7cyX1eP$8P7y1&pHX!Z&kX=gTPW zuzcqt*S3Eg-cjy<>;>UN9z&e=?(i-WdPl>FJip&dPn2%ib@ct0lP;MQtvu$7vPGZj zOx=@*hb;>o@8a+@N9mr_*5kHNc!=xh6|3^d`}_xi&A%&~tXCWbN2}YH-<5v6F=)Ac z`E=k?zgIqk;e{zqp#Yz{^!}-k?f0OxG~(`hHRi#kw}b-?pQYWwtl8xo-rD-{_H z^@l#;mBaP>d4+;t5nU;kQF1U!eIp+h0KtzTA6j*d6hY}4FL?9N-g9?k002U|zsCmm(|6bI_3NjXFna+2B^$TK>n8iD^VRBF}b_eUxLCfvV# zF`Dw?T{U!8&8}A}Dk>=M#CMR@!p;|m12QgjM)KuC7RZ-1{TKd38!uATiT$LJy?RIDA zKOfu`ADxm)$$=rsgZm|PsV7@bo(^=hpC;m?^sGfFDyS$F`|&AJy0>5BA+moz{)l{{ zh6b=kIO^B_+?*F;F-wMKuYXZ8G?khuglETtvRd5lwJkrXtZ8d}BW==E1C@=bwP9--W+*TqNQ@l4of=CN)+sa~?aP*h8@{H>i>!u_2ps zQgT_k*QpkZ-{&9!I2aH*ug;*|x;o-#oXYj&b3MY>B#|MBXn8_;0E7!rKdZu9!rA<- z3{~Md@A1~H zTa441>$9Y#DQO6pGKLj(mUBw-S*ok~HZD?>G%ixKMSZQ=^Y^+{&fes=7azdoTxA+g zGr?h)YT38_^$nl(aw4^)_iWz0XTsve6OQfJajf@m@9XNUmwo5WwIA8B;|QhiB7gG( z?`_@sUUGi#-zhQ!RS#|E%vtItnnxW>OpgX5rboR?eN5fJ{VrvT^@j)x_zJf`>T`k* zoZxTd#cTV%_-)@nciDx_k9Y4r;>hG@G95>NWSx7g zTU!zmT7brQ6>p}u?1^V9fA{CN-~L_Yvt@lBU-6xpb8fl2`0iWg%=}K@$NK9Q5|ADG zMe+UZn$UL!sVvd=u_F%YeX{LjAY_f}`F9tl!QE=dufI53lfg!qDGkIsLiFbnqH<&sLvS*=S{LX-r>z4JMb& zTytghW9Et7ATzJG{#X=!By(5GN^<(Gf*draucfk{Z&|xm_#LHPT`xy<5e;Kat{9GmL1jg`ipQ!`pAPO zCq}bGSwbx>5Mg#VHhuO*XD|>mG&v^yYnP^Uw) z*1fVXrO>~S)v!y=VzYc(X<+-8xb)}haMevt!cFR2?i18(7#A*sJLF2^o=V=Y1C?H= zC_lp96Rwd*Fl8T6rzJ^-Wx4W@cK@pTuLkHP>|{gL1?p33Iqfm^hwAlgZH;P**Q2HQ zyehC0E;yfSxf4Ew7367~`ujwN{Ql_Q@5;-aIV&%|M0Gd{*bx7dACmSPEa&@_k5wsZ z?FxU_vSmwSOo_$Xw1X1TOW5t+Qr7!zUT2MU)SMsWL$1m&cox)!Ge%|G?I&)_CkF8!(gHwwn?YY66L2r-E7QXFX^#tcB04Q~#?Vpz=2O~@ox#$cljD`bACiDN zm%f~n8OL^(UBFi-CRu@Kjf*D>Mn< zX^IaV9in;l$A=OVLw0NPXAOrxZ9E$40c;sO)w&kH_Bupwska}p-fDFk`ROTE@ngBxmdkoq zqv@p}y>vgl^f0|Ngr;P^HHffq%sVH19_G>-$uQ(Ll)(unncyAGp#F_~{MW}P`i#0S*I z%7W=F$!HW~563cB@6&2qYp2rP3&LXoT*ZfHYr6tyX%wrp3Wrr&NmSaU_^~GWd!Hwn z&#eQ~r!Khtx=Y+CWboS4v)%3=n9QVA%EQk56zeCuw{HIJXNo3?_h0;Wdia9ScF>`> zNd+Eh>YDd-tKeTCyDX7FjEehZ6RH9Xhr#ryVO>oId{e4E zIA<~N7rT0~x*RF65s_k24PDRUN_D6DiR`k*C$3PpfZTRyZllGhb!ru@PTuDepQsP1 zS?Vyf@K0EO>DI+lmA{i=o`qF9K(%{4_*s--sjd1|#-`DVXoor2cyh$u^5#ohUij_a zR$FX|b#Ovc6LfXvO1~>^ds9MQ(c%SjbKSwV618RXWDx5~YVq7WuG5@?YZZ?Ui^5(u zhJ(8%i#coCu?O4rt^LN6nAX-Wy%`^Q^JviFO3j|Xd>SHX2S$~&S0Iil2JOEVAtLQ* zWlijg#nU1LXydip~gSMVhQ zA$-(n{s4r_)*eI;uNdL;p58%@&5&NFik*nNw!Gtm5px?yKFm75Dm(YtBi^l|3NwwpAOYiPVgs@D$nn+YeU8fo-s9?z}|YB&r+Oo zsARHm=25>pY#)}H^nEkQdUMph6UAYd3vQei^P}9+ER*p|RswKa9$smCa{bz8j-(Vz z4&;-uq0q?u{IqEczB^|w3X{5czCO>$E!m-NQ}4mAlEOyzFes<3V&fUD2{C9hF<|OI zB_Iq(Y&L!kKgum&)dQ4REmwE2yr^2Eu2L7WAtypZNe|Kn8H7mL$kSh_HAZ`pQjBxd zLfkiV)TJzo4BU?y)-8_PmM-;KM@*YFl&CXj&3;SMUf%J?k{B?m+qV7z?A=SG0XQ}| zmlmaGWjYS;-@9RhFK$RiV$(l%y^PeVYtZ4_h-=;dvWVW&pS@DoIvr8)GpvPT7>aUrD#8D_Vbd`V~m=6?a{em7Z|&(Ek0c``h(5eS;DlJzsp* zq*Ig(7B()uDZfiN-+GBGKOQu{vtiu*J0R9Yj za)s?vJ)L{uIg5{st+s^9*pOa%ry1B zd#+-n-T)-IR=M2}x^^{+jhNgcH8-qlgC;(Jq0bw+>O~?{Rl6^_Ng0=a`4DFO=oyzQ zO?%&^uimTvo6;0t0N&Er3!<)%ZcO7d!a1zfe4mp3@ZfaU zU<1mTuB4|X4Mn~rCZ#K}@zLu*tE=VSn_1j5BdmxXe&jmld^tk*U8O5>;VRy)zZ$Q- zj(+zcT`%n*8yLs0n}&{fr-*yno#AE5I(^&NpknVoAfDh(Tz~ScI!~So!YV;gB5VId zh(Mech#lISNtr$lc*_T?Y7eT5Mx|G=GjJcnqpfOdVc1;<$$7FGCekCI%&2I-krZ%t z<|y`UB19#qfMI=`Im*4WOy*cGhJ7+Vfi=o}nPtTYu!Y9tg47il2OERF#ys*n%nv~y zp3jlPnIGa!%SU6c!h(F3T#QPp(*PunN=aKewm#FNc-6EN@>NM^>$m7oVqx{uR_oKd z3li%w{Wjv39#Fn@YiXH(Yh`)k{{4ZbVBP*keZaoj-GRnB2RR~kXI)dEx^|y_EgRbb z@azW>Z1Kpa1SHr@{?An7nQ8}l=K!&;pG4b|oLx3&dy6l+wLM-7BqZB(;%Xp1{S;o+ zG(V-H+C#xEO}A+<3qsyZm3l)-rtnbt~6puc`TU)n3@wwm-;jkQeU9 z?1^L6U5)-C#`Z-R8pQl)V^MRfsr@W%Yv_-mKZUl1Hj}PaEv`P=uR_bH6m2uw$k61_ zlu$Y?FZ3{KndET49eT<5#E8&^p^VT4p?Ms=ja|!O>W1N2Gs7426K}H7m>b%s{CsZs z#;BkCxw?Aq_Gz5nG*0XiBgk@xTB&$?T1kOnnnB%a*?K(jj$Fh1F1IX7Z26TeDsvM4 z*z(KciHmyQyCd=VFZ1{+p?JML{A0^YUwrAkt-^YY0qC<18xucrNQrgG$oTV9O04Hy z5I-`h#KE%*QsD_sO*h_58X2FS;#cR-mz?+CA}u8K<`LvrJw$C68(Yj^7EKy5(o~b8%pGcI@O_svgRyxAa^(LN*d~^ z`5w8V-}>I!4TK(2jr>ZHEQHoPXr2h@)CGkm~C1Q-6+C?cQy5n<&=SV4-0;G&=h&lRo2%DYh(9hs&?qgcwm ziz5b`*B)JBh1`b9c9JJ$x0P6-Ywzyydo$hcq<$9VB>Lk?#nOKW;=J$kBzYcx?tPxa zVJFd7zDWM~qQZTi%Bl5v(@(z$8aoPkXhUv@B$fRd$B^PAx zt!)S0=0{3;1%mh0Puu%^FXCL$T7f3|QTGIOXnYve#An89zxh1280S&*rEh+b^Y|^_ zd>`j}6K6b+Z}wm^icRhc=SDqBL}u5z$q1ATUb~9doa9+IMB3*(?h&t(x^t%|?2O&= zOqF;sU*7WA`T`@hXBE3*ejvP#3T}W7gXG#|P>T96nVdwOge9*ius$ zm^}qG)zHKB7_XNsp6Wo#xa+2)qMIg~X}G;)yFD|t^UY0+{61C2e_yoM>wTd)W!#*6 zRG^&^`_4S%(dZlba5dkh`=f$wXqKVXP=+@(o#+g$WeHL%r#rm*|Fm{C@KKc4{(ok6 zcC*=Rk|l%~Lc{1(aY;7GvYE{Ozt8MOt=Hb$&;Rq^2R578naRvN&vTx0 ze&=`2vCA#p#@fqOwXN;M$DO7lV;rUFz%+TnD7-vb`8wXwN3eG4@DbyuMP5MLV9N6TE}2QX1Yso;^dJ;v3L@y?THkdILE!#Crz#lxZAxaf&4htK)<2d zHAcRVe;KCg(9D4WS$2!B1AT@%Y%~XO$yy4z$kl3x>dwp@mY}Y4*x2B)E7f+nB}I$r zuUihLJgokvWf%xzMaY<*G1m0{MJ}5b#(#0ET%F%Ggb8UuYJa6XNYMbDCKp znaX(gerW!RB{2{5^f?o{RIC9=ZNRgbXRbAWN!o%~TWsFN02Mx;lOEXgPS`zeVX5>~ z2rrpVsSJ$Az|R99QOV9vRV^Fb8hzCu7pdzUEU{Y5r=`5#$RZNY^gylpUnpK{yO&G= zFSFpp+CgQzvc<}oJac9_IZiiYhUJRawE6J&^9k8!LEJ*i6OA?rkC%W84lopRm^&zz zvTe5IdxAA(=pGhTp~`W(CFmJ>PLhARl12NO)rPC9vs!0FuQ30^YR64lbg|l=b!UnA zOv#eNl9l4MvpJJ*oPo2N=|PJP%R0wT3dS1yzcDP``}W}_?n=qcza9W4t}ECV=jYmw z?Sr$aJmgD4Me6D&pLu#_Mn+YFtdB}IuPM!7cnvl7`QiEB61{)N}h=byP2%`F9WZ#jj!>_fs91bHDCotuHaLL-WO zBR;1eGE!6iUq&jzi~%jY&ReHALm&T+kzRG~KyS#IlI#$H0E6w?9MfoHj5Fo6h*0`Y z>Gxy&u2NU={<3<#njLTVs2`{;ay#!mwrpEryp6Y|vi!|e=c3UqP`A4dQ}4g}x=VK-nfza^yg{^~C{}tM!(9Y|RY>+}DDp zQVAA+LbcLGQU{|qkY@Y(?A5WVhUQw1=P0rCQ@tB%&99U?Wd7P+o!3S*`qTl zbf9%d!tRvx1g9%GHRqp+fwjaJl3YaoAYq|etpK)*{%T?OQG&yNwP{emGAmIvP9JY< zJW6D0Z%>#NN02Grh)lS!aO%{HJZhx%)mObWhYqzz)YLH(!jYp#j(+?_DB?bM+}Lrs zp)?o%Yy;W@D%9V%a`0N?Y++bZQ+whTv& zd7Skp*QXX1-V(Eb4rsreTzL2|WuTx|Zm8r<2M;!!;_sU6hYCkKa64t`u8xj+xxn;g zOuMBVI>n1tdr(;p!2STx^--&B*KVJdadK>NvunROTdI?x~u=%+;JHJ#H*%s9Nk$$ zuS`h`4?Cy#=#FQlm#FqH$D^6a;u#;Ft{1*Gr|+@tb9T;=7U34F>F)geE5|TlvNF1y z-Dj$l_QSfML`0|LFS>qFdH&y@<1sZ&LfzO%NOq^)ZObmdX6bZM%|w;$aq)nCX=%Z5 zn~>IoT3NZg9BLV=E^WQ7C^twy+!ix9HJm{0_9}alBbciKtq+dY(~lf^^Tn-Ox4!&Y zSQ}No_@>3va&wDsD;0j(>^EPag+F+<6fEH0C~4V-9cIo;ho0PYl36zEtOVn9&(^VaiS$@5*q z^DW`|N_|e|7#)^(y#BSf8b0Wjq@m!^`rodv!phWU&hL2j$?5pIVlkfu(dF9qLx)c! zlt_3*Eg(cG`YWHUHYbUcI_8MNLtkQ!jkq8;cL>+e?i*WQh#PBFd~)1Cf%IBct<}Z6 zG6*f>V7ol)ntxdW9+!~w(rfN0#pnB>XwuqjndP@s@Jk@x_6fgjWyN`ylt@>NT3^qD zZ}63pmbz0sFN&z?kW*OSW>jfiM-LpJWMd3syi42LP&oOQ)8jq!TlB!Eu>YRYlj`f9 z+2Hg0p6|?@cH4CF!yE%#I0mnEgL3PwJk=1NPg2c7T5(G}ZuLRLm>9f}h zNHI;m5~Whq>RQn@uNNp$YWKWr*;rRaR8R^u$$-EBSih>Z2Xt~>Z(s~6*@a|%2XPl+ zALSGEST&_K?xThKxP$xn1^00~_n|a5e;=x~lZwV`%qPe&bvB-8kvapYd%Khk8)mV6 z6Q)FrLb1$TW$HZc4K=gL8vpmGo%}8_?_V+Qq=0!)S?ixgINP7W>uTcdNprioN7+a% z09nH2Cj7Rswh&k7MpD<#@`ke8!mkfN*geD1OXBTf(hk&FN6h3LH{vOZHEDe;wYD!| z`^{giCY5v_=byo!y_p3m)>bQi%e3()aiNn(dx8hw#;mHcceVySqtmNKz@a+z&T)^J zJOzyux@*N$;pyz~rMKWV_^8ncjlyq9R_;qe#``T$-n}JqQHfbLuk>b5Sv&(Vyvu5_ z<>tlopH9i{sP%gH;%RN|^W;pPb=#UXYszBFr|^xMH#x`l_q}^N5lpG)=1#tP_Uzfz zA(%TkHwnrg3c+J4!fgw)LMYW&TWh{_?F_0~LK+Ep*f0nQ1Fw*(b#-+Dn5nAW!(A9g zctGkW7{{(iq!(pHXw;6!9_k+it50)rZf2Thdq5d5syPkVegaii23g)rvzq12M`iTkzU$$qUwQ{y^w`0d_seRZ70RodXF)Eyf*oRQI^iQV(MtHj9X{`WpRSi%)3u*0 zx!*M_|LGhir5C1|$5l0CwB0mb3;yj%j5hsJkb*H3o-+aJbMlfyM(Z1#opoXu z)x}gLbh5o2k?@1dC~rxUtxcQs`!SYs)O;mOU_yPoeW|9YZfm<6sJ&8M5^q0L5x@9( zE0qSE%x@_%G_6f}4Kz(o+!o~>DQ9KlyP#Eqt(d&Xci@a}iMKy0^AYZ(CQ%M3hm?<$ zKt)`rbV1!|QOecA@yWWHT(GD_)_GAE$;#R>g=e$asH`ZQsaRwki>skgUVV$$pvjUD z_b+F+^_By88zSjNKF8W~e0~al_kLEC$C=PFVm#I!K(pFx2_nfhvxBP*ee<&7f;O@r z8MMUb{SFzFo#wmvJ7o<^%6Ca7zdbG66KpwMm4q03>p;+xa~a*Ep9B1Su)zpPSyhP0 zx+EuLG=3y!Z^kEYe&`WXE0AqSxT)7)1?lp0$sBch(FDF*hFuhwVc#jFq+t83_rD3Y z6e-vm_wHr%)OKV~`RNQER!k(u#N#IC;wjm^uktrzGIWd=xfU6oJGpET@((FE{i?~i z=Sq?flWnkQv)!{~h-nF=Fa!yhQe_ZzqB`zSPtgN-n$sJc4Ql}&q#hyxK z6w@3Wpcv+0G-_y}(3in}!>~a+>FetmAPmN>nSnri;LCn;R3)dm-MH({imS~?@7X9u z9;Lq+cS1#>&Ki|Y!1-BO&J^>2f7JZKopuIR1s@-mha`{F%GSi=8189$LvuQ@CIt?pSsLq7gZg>%?1zYV= z#MhxbutAMM9bL)qF^M~i?-?Dm_8hL_(%_0?ha+GqiaM~?`<;UPpUCWCgM;4|??AHn zM|=9KsKsaWoM9D^8FzFvc$v`)nNM#a_iZ30#$fgELAgHIbBO_i4&{oZ8uS!nkE_=s+75NaYhedl(v2xm~HHv^A|vq=1@ViF=v)vkLZLjEDX-3&}$q z@PNAN$BSeh$=A95wF!64l=P1oKQCj~jjR>>wIw&KT)MPGx}$VkbAEoaK8_^SVL_Q^ z^g4oqWtduFy3+Ug>o{j9iGI>3=;u)6$?GL} z+-NpqLAb$xFN(YS%W+YpaYQe`awGeUBbPapNl7JOtOo*`o+A^fi#~&A)=ebWVTAA7 z$CR{7uV#^%o}ZuUgt_R}Z$s;rP(jO3$65%GET2kQKxQtTNlTz`n>2rAb!XXv9M#A^ z`>{;9Z`z-DK>Of4Kg_u#N&5iV;g|ytUmSM1!iYn3Lp#uZgsO}NYOlkSl9E7neY3YK z80@9aqeiuCm;QieMu<#^RY%qQHkscK9aK^*9~jNpAgdqAEoHCDXA1!k{Lty-zf3`Z zLH8s~=4h-ws*F*)g5H+Q%UQhB5l3eCDPabhN16VZ@tBGn$O@KW^)fRPI?^o zAZsWT!=HF`oh%A>18&qM7Sj;uDLO&hfAoWW>m&!i5W-Wt*@+B&-8Z73V9vt%`1R*6 zoKsM6)p7t-9NzRj zC_w4H5!<#s`^PsKMQ{G`*=^fiHzqNxCmFBnBdA=RXb~+F31d*QC!x~hk5Vb8Krty# zDs{zhvJ+q;jE6tl$|x_fZ_ZnAX6XOUUQh>w0KiTGfL z9U(K=cP6MmE$W)nEq*NY| zpCp@Pv(lsygljpo8;FSOLwO_YPZq3R_rIKH`GT*`GcUU0TjvR*?fr}Ax%cby6#NV4 zxq$n>_*?y`kgHLA2psV!Q6zr&uU9G4m5Y#UNuk2RT>D8%@SQl_8y{+q4gP6x$>2{0 zR}Y2yarTw!UYDu2j+Q0TsC@LzwVvL7fsYpPZ1TpJIsk1RT5_8!n$kI&~9s1YmPdOXLf9o2jM{`&l-e#otNALHE$4cQVrbb8VNzKhC?YV`8 zq2ALcn)K+|E1N_8)g3a)#;?*%}CiMTcw$|erb44jvC$H#6u5FTal@hL+ z$@SeLuZ{|NjzlNg$iM)LlxL1fxyXUjWU&7A{^c^a)w3 zD__Zka=d_u!@F^O&sHwtJBL2(@J*GKWL3L%N{&PO96IpZ2leatvz1mX!uLtQ)dMK- zvbi4JDhc?f*N@{{rVP;fhM=r*isfSp7Q#S3qmOe(D&W`lZtncf*EUO?Sz1+0$4? z@6LGW;YWV`^q=ZKIx~Q|H}Z;W=Uq2zWOMV{5}3h@Rj-$bqh*rpWVIjJ9{a1P!N)84 zkvBwt?0)RHnwvNAd-LzOX_BUu(oxTf>crbu-hAEk$vFgORVAv4BXTBBzwYLhB)PBV z+bxt`pLEk5^QYupoP3-q`(sYCR8L%Te&AhL3&+}V&0H!ANRq*94$A3%X`#_NFv+E! zy&u2BZ`0N+xjIYQZ(T>>VnvFIiwk}<2M?~)JX)$M)fM4-npa>7;Rvljkz)vC(MAIvT;7~}44d2iPfWWal3*LyA9 z?lA?$vwu)oxoXv|Kblp3<+zl-z=?PE*6FW#i^x>*S!bWYilI5(iMByAJ)LZnBnKvX z4Tex;02(v3t`bR0ohl&#o6GH_c->%M=Vz^L@&p+fsSeXP+uzqeV3JTW5@~Jyw7u(0 z_m^E=J!i?gL4m!VE)pDsx<3E3wKWn^5);fwe;+k19H|-l^BAM?%FM%a$TgcI$Vd~5 z{+E)=ZiS_&nrCCyhOOFD*}?3gO(IZ+9hWKmph<;|c*T`QA#k48M$2J$grk|t>IJ&D zM5@ic5*at*Pe^lzO*3Gm>0YI>(x=qb`4e-?+YTIPERYGbyvmX#KHL@Y8DeJy53Sl? zvjv;+5Q(B8lHS*AsEg~CS;p;*buiu7TAbaiG{im~DN@>mmx8Q%wO4e@w=gLXt(hV- z*j~a+Igg{=94&2dnK^lpS1liXcsg7ZYKKJkAj@3pu7jf3Z1jn}K3n#hqwIzPyQ_r_`< zco~|F`Xf%PJy)hF8XMO?BAiuPFf59S_-I|JWTGc=rBcQWMdYh>(%b!SvNp|nQRgjI zcgPIxsA>QY)f8~%xyJ8P#zH6al!i2xMD8;l3%xuV#&cLs_6|%HRbAm;P5Nk2h8-ZQ zL9Ik_aipXW_F$*7b%-;@br3irV5lK<6#&Ssz5{2F5IMz4%WyiwI(Yp%c`eIHVW zY`+(qy&5?bwZc-2(k2mBn@^KLYTmpt%`ffU8w;!3F4hhI(Y$$OBT*y5@9%9&F3kP@ zoZ?wj7JJ1q-=qomx^kZ&?L59mYFYHRH~EyN3Os3IFLnw3Oi`;;G*uuNueKIT8Iron zOMz5Ja&pJMruKgS`E@l9KJ!-K&N~C|z4Ss&-3u?h7no9xDR9l)6?fddYSm8{luQY{ z^-PWadwVOY3_;W98hss~hFd@F=reM2KsQHb$evUahv($-^;vez7D^P~L$;BWIZ9-Y z%1koa+5kv9I*AMvB$yfO0}J&3J~kR8Z(=1+e$eTRWHN9JE+_uUUgUSj7OKv zU$pYpSz|NsDQkO@Di$o1Ia_{bS-RWM35V$=Sixa92G;=q<OI*3%wy0kAHNM$!vdU?9fop#LZaosE#elGmFm{~#?%2dytcV@Z9&RKT%J@+g)UyFS70^!H2qPMzhHf}#2(MEocAgHp7 z^yex3rA;U;7?tG9h&odV;Y&_*Xq~+(vAgM#u$gm_PR7M5?E7DjW#mJ`BmHueX&|^I zhix!oWTYmuN)2>@%YG5m{}0nj!nHaF2_>E(jBZ1E#8A8;3tRlE0$`vM7@!*)YK_>W zC#39dDax4E`WJl$z0`s;w87lX&yd;R6~(@UyrN)RULaHm#`hE;8s(_@8RF+|{Xwsz zE9?}`a1bQV=_J+z`*;Ysla`2-eo`t(IXNW#ESuh^N=pRAvsj9S>aQzKNa^a}num^x zak1-=t~~c4@oGt(IR)%HmGkE@&LCCGjL8I_y!Ncu>*ZmO+}}`&dk0OEObvd2 z-}o|mX+g9ry3khjAGsAJh|u}A2@}Zu<8#(NB55Iy|E89YEycV})S{yRHj#YEmq0S= zT>c+`8Lk(L7IMy=&Rwvg9DVQ=ashy$*#m)-m&*{*|75VW$RUQfTk0$WfoPz<;3u{f zWSl9H;EPJS7MUl==PVpYXJ=+lD8#+}QkevtWOKY-D0b|?)9$FN6Pf&vOCfzE@c+l$ zpgVl1mXGf+2?Ouc$^htKiKs%px3m-suNt&|*fg(KqfC^lhI+cXS$4Zad=$wpSF#xr z-K8#ywBg#6ARf{^qE|_WyGb0J26{&{+#@o0$8PhD0kfBV+jA0Vtj}qH){6-5?E)7A zIZMfd|Q|_nNJxrJ+HFNClPMyNi{TG8nl6T_GS0s8kjbtgc?Y`Ude=l(_)*&`KIaNDCAV zp-60I9Sti#rwq@%;9+3nMwW*2DJLoG`DnR4-tW;1r zMeJL!U|-Nj!m~NZmKvV;5&(HW-ULB3_`P^B_bM}ImRTAzD6m(CbyEV{rM6_7MQ3PxqN=cEa77R?sCLflvXyQM9 zEUnXrEfk9-6W8P@{Ez7n8H{9^7a^8eO==>%5Y31%Rs8|fzh$7hlg(?QEv#lMAf$Jh zrLadMAp8yB3|k>02fW3~3ctE2Lpg*ms z#cLfYeSKt)Al|i9lOOe`z2fj%M?`}(j8n~iScC!Y*(xo_1bSJ@aXY;%wOA>Uz3Md# zd7MTF@o1&k?2DJ?QOVh4aSr~;Q5(NHN|J{-iUX{-Zyfc_jOVex6&4Tss=dktQ7nRd pkhO!nA|n6P*ZSwsO7qnf%EEy1IO`gju^h=!hIJHUR&ZCm{x1Vk;28h_ literal 0 HcmV?d00001 From 3c69360cdebc2c3729bd7ec658284a5480af1f35 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:25 -0500 Subject: [PATCH 030/280] Conceal Qt's hard-painted superfluous decorations Qt paints some decorations directly and provides no stylesheet hooks to customize appearance (for example: the rulers in a QWizardPage). We set certain application palette colors to fully-transparent to render these aspects invisible so that the painted features do not clash with visual designs of the application's themes. --- src/qt/bitcoingui.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 67eede3129..69879e60e0 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -123,6 +123,21 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): qApp->setFont(appFont); #endif + // Qt paints some decorations directly and provides no stylesheet hooks to + // customize appearance (for example: the rulers in a QWizardPage). We set + // certain application palette colors to fully-transparent to render these + // aspects invisible so that the painted features do not clash with visual + // designs of the application's themes. Important display elements provide + // the ability to customize appearances using stylesheets which override a + // palette set for the application, so we do not risk concealing the vital + // UI components, but consider removing the override to debug an obstinate + // display issue. This code must run before loading a stylesheet: + // + QPalette pal(qApp->palette()); + pal.setColor(QPalette::Base, QColor(0, 0, 0, 0)); + pal.setColor(QPalette::Mid, pal.color(QPalette::Base)); + qApp->setPalette(pal); + setWindowTitle(tr("Gridcoin") + " " + tr("Wallet")); #ifndef Q_OS_MAC From 89942b1810990aa245f3f22b97f7127c8ee536be Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:28 -0500 Subject: [PATCH 031/280] Update GUI light and dark themes This updates the light and dark stylesheets to match the proposed GUI redesign's colors and widget appearance. It does not change structure of the UI elements. --- src/qt/bitcoingui.cpp | 8 +- src/qt/forms/addressbookpage.ui | 3 + .../forms/researcherwizardpoolsummarypage.ui | 2 +- src/qt/forms/researcherwizardprojectspage.ui | 2 +- src/qt/forms/researcherwizardsummarypage.ui | 2 +- src/qt/forms/rpcconsole.ui | 12 + src/qt/forms/sendcoinsentry.ui | 6 - src/qt/res/stylesheets/dark_stylesheet.qss | 379 +++++++++--------- src/qt/res/stylesheets/light_stylesheet.qss | 371 ++++++++--------- src/qt/res/stylesheets/native_stylesheet.qss | 9 +- src/qt/transactionview.cpp | 1 + src/qt/votingdialog.cpp | 1 + 12 files changed, 410 insertions(+), 386 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 69879e60e0..4f01acf901 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -639,6 +639,10 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) aboutAction->setIcon(QPixmap(":/images/gridcoin_testnet")); } + // set stylesheet + setOptionsStyleSheet(this->clientModel->getOptionsModel()->getCurrentStyle()); + connect(this->clientModel->getOptionsModel(),SIGNAL(walletStylesheetChanged(QString)),this,SLOT(setOptionsStyleSheet(QString))); + // Keep up to date with client setNumConnections(clientModel->getNumConnections()); connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); @@ -653,10 +657,6 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) // Report errors from network/worker thread connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); - // set stylesheet - setOptionsStyleSheet(this->clientModel->getOptionsModel()->getCurrentStyle()); - connect(this->clientModel->getOptionsModel(),SIGNAL(walletStylesheetChanged(QString)),this,SLOT(setOptionsStyleSheet(QString))); - rpcConsole->setClientModel(clientModel); addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index 06659e4a4a..07c016ab55 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -47,6 +47,9 @@ QAbstractItemView::SelectRows + + false + true diff --git a/src/qt/forms/researcherwizardpoolsummarypage.ui b/src/qt/forms/researcherwizardpoolsummarypage.ui index 19529892a3..cc9a84e639 100644 --- a/src/qt/forms/researcherwizardpoolsummarypage.ui +++ b/src/qt/forms/researcherwizardpoolsummarypage.ui @@ -264,7 +264,7 @@ QAbstractItemView::SelectRows - true + false Qt::SolidLine diff --git a/src/qt/forms/researcherwizardprojectspage.ui b/src/qt/forms/researcherwizardprojectspage.ui index dbf81a937f..7808c7a5b2 100644 --- a/src/qt/forms/researcherwizardprojectspage.ui +++ b/src/qt/forms/researcherwizardprojectspage.ui @@ -56,7 +56,7 @@ QAbstractItemView::SelectRows - true + false Qt::SolidLine diff --git a/src/qt/forms/researcherwizardsummarypage.ui b/src/qt/forms/researcherwizardsummarypage.ui index 0a95056716..4a62ae1bb9 100644 --- a/src/qt/forms/researcherwizardsummarypage.ui +++ b/src/qt/forms/researcherwizardsummarypage.ui @@ -859,7 +859,7 @@ QAbstractItemView::SelectRows - true + false Qt::SolidLine diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index f2b64d9a1e..2b4ff8be2c 100755 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -712,6 +712,12 @@ false + + true + + + false + true @@ -767,6 +773,12 @@ false + + true + + + false + true diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 1e714bbb42..f9bb87325e 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -13,12 +13,6 @@ Form - - QFrame::StyledPanel - - - QFrame::Sunken - 12 diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index bd001997b1..afd33e666f 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -1,159 +1,185 @@ +/* Copyright (c) 2014-2021 The Gridcoin developers + * Distributed under the MIT/X11 software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + */ + /* general */ -* { - font-family: "Inter"; +QWidget { + color: rgb(204, 208, 209); + font-family: "SF Pro Display", "Segoe UI", "Inter"; } - QMainWindow { - background-color:rgb(49,54,59); - border:none; - font-family: "Inter"; + border: none; } -QWidget { - color: white; +QMainWindow, +QBalloonTip, +QDialog, +QMenu, +QTableView, +QTreeWidget { + background-color: rgb(26, 38, 50); +} + +QHeaderView, +QListView, +QListView::item, +QMenuBar, +QScrollArea, +QScrollArea > QWidget > QWidget, +QTreeWidget { + background-color: transparent; + border: none; +} + +.QFrame, +QGroupBox, +QTabWidget::pane, +#SendCoinsEntry { + background: rgb(23, 30, 40); + border: none; + border-radius: 0.26em; + padding: 0.75em; } /* HLine */ QFrame[frameShape="4"] { - border-top: 0.065em solid rgb(67, 74, 80); + border-top: 0.065em solid rgb(58, 70, 94); } /* VLine */ QFrame[frameShape="5"] { - border-left: 0.065em solid rgb(67, 74, 80); + border-left: 0.065em solid rgb(58, 70, 94); } QToolBar#toolbar QToolButton { color: white; - background-color: rgb(49,54,59); + background-color: transparent; border: none; border-radius: 0; } QToolBar#toolbar QToolButton:hover { - color:white; - background-color: rgb(65,0,127); + color: white; + background-color: rgb(26, 145, 235); } QToolBar#toolbar QToolButton:checked { background-color: rgb(64,68,82); } -QToolBar#toolbar QToolButton:checked:hover { - background-color: rgb(65,0,127); +QMenu { + border: 0.065em solid rgb(58, 70, 94); } -QMenuBar { - background: rgb(49,54,59); - color: white; +QMenu::item { + padding: 0.25em 1.5em 0.25em 1.5em; } -QMenuBar::item { - padding-bottom:0.5em; - padding-top:0.5em; - padding-left:1em; - padding-right:1em; - color: white; - background-color: rgb(49,54,59); +QMenu::icon { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + margin-left: 0.5em; } -QMenuBar::item:selected { - background-color:rgb(65,0,127); - color: white; +QMenu::separator { + background: none; + border-top: 0.065em solid rgb(58, 70, 94); + height: 0.065em; + margin: 0.13em 0 0.065em 0; } -QMenu { - background: rgb(49,54,59); - color: white; - padding-bottom:0.625; +QMenuBar { + spacing: 0; } -QMenu::item { - color: white; - background-color: transparent; +QMenuBar::item { + margin: 0; + padding: 0.25em 1em; + border: none; } -QMenu::item:selected { - background-color:rgb(65,0,127); - color:white; +QMenu::item::selected, +QMenuBar::item::selected { + background-color: rgb(26, 145, 235); + border: none; + color: white; } -QScrollArea > QWidget > QWidget { - background-color: rgb(49,54,59); +QMenu::item:disabled, +QMenuBar::item:disabled { + color: rgb(153, 161, 163); } -/* horizontal scrollbar */ +QScrollBar:horizontal, +QScrollBar::vertical { + background: rgb(16, 21, 28); + margin: 0; + border-radius: 0.26em; +} QScrollBar:horizontal { - background: rgb(49,54,59); - height: 0.625em; - margin: 0 0 0 0; + height: 0.5em; } -QScrollBar::handle:horizontal { - background: rgb(150,150,150); - border: 0.065em rgb(150,150,150); - border-radius: 0.19em; - min-width: 1.25wm; +QScrollBar:vertical { + width: 0.5em; } -QScrollBar::handle:horizontal:hover { - background: rgb(65,0,127); - border: 0.065em rgb(65,0,127); - border-radius: 0.19em; - min-width: 1.25em; +QScrollBar::handle:horizontal, +QScrollBar::handle:vertical { + background: rgb(20, 67, 124); + border: none; + border-radius: 0.26em; } -QScrollBar::add-line:horizontal { - border: none; - background-color: rgb(49,54,59); - width: 0; - height: 0; +QScrollBar::handle:horizontal { + min-width: 1.5em; } -QScrollBar::sub-line:horizontal { - border: none; - background-color: rgb(49,54,59); - width: 0; - height: 0; +QScrollBar::handle:vertical { + min-height: 1.5em; } -/* vertical scrollbar */ - -QScrollBar:vertical { - background: rgb(49,54,59); - width: 0.625em; - margin: 0 0 0 0; +QScrollBar::handle:horizontal:hover, +QScrollBar::handle:vertical:hover { + background: rgb(75, 143, 226); } -QScrollBar::handle:vertical { - background: rgb(150,150,150); - border: 0.065em rgb(150,150,150); - border-radius: 0.19em; - min-height: 1.25em; +QScrollBar::handle:horizontal:pressed, +QScrollBar::handle:vertical:pressed { + background: rgb(58, 132, 223); } -QScrollBar::handle:vertical:hover { - background: rgb(65,0,127); - border: 0.065em rgb(65,0,127); - border-radius: 0.19em; - min-height: 1.25em; +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal, +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + background: none; } -QScrollBar::add-line:vertical { +QScrollBar::add-line:horizontal, +QScrollBar::sub-line:horizontal, +QScrollBar::add-line:vertical, +QScrollBar::sub-line:vertical { border: none; - background-color: rgb(49,54,59); + background: transparent; width: 0; height: 0; } -QScrollBar::sub-line:vertical { - border: none; - background-color: rgb(49,54,59); - width: 0; - height: 0; +QSplitter::handle { + background: rgba(58, 70, 94, 0.4); +} + +QSplitter::handle:hover, +QSplitter::handle:pressed { + background: rgb(58, 70, 94); } QToolTip { @@ -162,39 +188,70 @@ QToolTip { border: none; } -QTableView{ - background-color: rgb(49,54,59); - color:white; - alternate-background-color:rgb(35,38,41); +QGroupBox { + padding-top: 1.5em; +} + +QGroupBox::title { + margin: 0.75em; + font-weight: bold; +} + +QListWidget, +QTableView, +QTreeWidget { + border: 0.065em solid rgb(58, 70, 94); + border-radius: 0.26em; } -QTableView::item{ +QListWidget::item, +QTableView::item { background-color: transparent; - color:white; + border: none; } -QTableView::item:selected{ - background-color: rgb(65,0,127); - color:white; +QListWidget::item { + height: 1.5em; + padding-left: 0.5em; + padding-right: 0.5em; } -QTableView::item:hover{ - background-color: rgb(65,0,127); - color:white; +QListWidget QScrollBar:horizontal, +QTableView QScrollBar:horizontal, +QTreeWidget QScrollBar:horizontal { + border-top: 0.065em solid rgb(33, 44, 58); + border-top-left-radius: 0; + border-top-right-radius: 0; } -QHeaderView{ - background-color: rgb(64,68,82); +QListWidget QScrollBar:vertical, +QTableView QScrollBar:vertical, +QTreeWidget QScrollBar:vertical { + border-left: 0.065em solid rgb(33, 44, 58); + border-top-left-radius: 0; + border-bottom-left-radius: 0; } -QHeaderView::section { - background-color:rgb(64,68,82); - color:white; +QHeaderView::section, +QTableView QTableCornerButton::section { + background-color: rgb(23, 30, 38); + border: none; + border-bottom: 0.065em solid rgb(33, 44, 58); + padding: 0.25em; + color: rgb(115, 131, 161); +} + +QHeaderView::section:first, +QTableView QTableCornerButton::section { + border-top-left-radius: 0.26em; +} + +QHeaderView::section:last { + border-top-right-radius: 0.26em; } QHeaderView::section:hover { - background-color:rgb(65,0,127); - color:white; + color: white; } QComboBox, @@ -245,6 +302,10 @@ QToolButton:disabled { color: rgb(174, 180, 182); } +QLineEdit::text { + color: rgb(174, 180, 182); +} + QComboBox::drop-down, QDateTimeEdit::drop-down { background-color: transparent; @@ -261,9 +322,8 @@ QComboBox QAbstractItemView { selection-color: white; } -QAbstractItemView::item:selected { - background-color:rgb(65,0,127); - color: white; +QAbstractItemView { + alternate-background-color: rgb(23, 33, 43); } QAbstractItemView::item:checked, @@ -273,23 +333,6 @@ QAbstractItemView::item:selected { color: white; } -QDateTimeEdit QAbstractItemView { - background-color:rgb(49,54,59); -} - -QDateTimeEdit QAbstractItemView::item:checked { - background-color:rgb(65,0,127); -} - -QDateTimeEdit QAbstractItemView::item:selected { - background-color:rgb(65,0,127); - color: white; -} - -QDialog{ - background-color: rgb(49,54,59); -} - QPushButton { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(44, 160, 247), stop: 1 rgb(26, 145, 235)); border: 0.065em solid rgb(23, 137, 223); @@ -317,32 +360,6 @@ QPushButton:disabled { color: rgb(211, 232, 248); } -QTreeWidget{ - color: rgb(255,255,255); - background-color: rgb(49,54,59); - alternate-background-color: rgb(35,38,41); -} - -QListView { - background: rgb(49,54,59); - alternate-background-color: rgb(35,38,41); -} - -QListView::item { - background-color:transparent; - color:white; -} - -QListView::item:selected { - background-color:rgb(65,0,127); - color:white; -} - -QListView::item:checked { - background-color:rgb(65,0,127); - color:white; -} - QToolButton { padding: 0.25em; } @@ -374,51 +391,41 @@ QSpinBox::down-button { QDateEdit::up-arrow, QDoubleSpinBox::up-arrow, QSpinBox::up-arrow { - image: url(:/icons/light_chevron_up); + image: url(:/icons/dark_chevron_up); } QDateEdit::down-arrow, QDoubleSpinBox::down-arrow, QSpinBox::down-arrow { - image: url(:/icons/light_chevron_down); + image: url(:/icons/dark_chevron_down); } QTabWidget::tab-bar { left: 0.5em; } -QTabWidget::pane { /* The tab widget frame */ - background-color: rgb(58, 64, 69); - border: 0.065em solid rgb(67, 74, 80); +QTabBar { + color: rgb(220, 220, 220); + font-weight: 500; } QTabBar::tab { - background-color: rgb(49, 54, 59); - border: 0.065em solid rgb(67, 74, 80); - border-bottom: none; + background-color: transparent; + border-bottom: 0.195em solid transparent; min-height: 1.25em; min-width: 0.5em; - padding: 0.125em 1em; + padding: 0.5em 0; + margin-bottom: 0.5em; + margin-right: 1.5em; } -QTabBar::tab:!first { - margin-left: -0.065em; -} - - QTabBar::tab:selected { - background-color: rgb(58, 64, 69); - border-bottom-color: rgb(58, 64, 69); - border-top-color: rgb(120, 20, 255); - border-top-width: 0.13em; -} - -QTabBar::tab:!selected { - margin-top: 0.13em; + border-bottom-color: rgb(75, 143, 226); + color: rgb(75, 143, 226); } QTabBar::tab:!selected:hover { - background-color: rgb(58, 64, 69); + border-bottom-color: rgb(75, 143, 226); } /* RPC Console */ @@ -426,15 +433,6 @@ QTabBar::tab:!selected:hover { font-family: "Inconsolata"; } -QGroupBox{ - background-color: rgb(49,54,59); -} - -QListWidget{ - color:white; - background-color:rgb(49,54,59); -} - /* Main Window*/ #toolbar { @@ -467,6 +465,10 @@ QToolBar#toolbar QToolButton { width:100%; } +QToolBar#toolbar2 .QFrame { + padding: 0; +} + QToolBar#toolbar2 QLabel{ border:none; padding-top:1em; @@ -517,8 +519,9 @@ QToolBar#toolbar3 QLabel{ #researcherHeaderLabel, #stakingHeaderLabel, #recentTransLabel { - font-size:15pt; - font-weight:bold; + font-size: 15pt; + font-weight: bold; + color: rgb(250, 250, 250); } #availableLabel, @@ -576,10 +579,6 @@ QToolBar#toolbar3 QLabel{ font-weight:bold; } -#SendCoinsEntry { - background-color: rgb(49,54,59); -} - /* options dialog */ #OptionsDialog #statusLabel{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index d56eace429..133b2da136 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -1,17 +1,53 @@ +/* Copyright (c) 2014-2021 The Gridcoin developers + * Distributed under the MIT/X11 software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + */ + /* general */ -* { - font-family: "Inter"; +QWidget { + color: rgb(58, 70, 93); + font-family: "SF Pro Display", "Segoe UI", "Inter"; } QMainWindow { - background-color:rgb(240,240,240); - border:none; - font-family: "Inter"; + border: none; } -QWidget { - color: rgb(88, 92, 107); +QMainWindow, +QBalloonTip, +QDialog { + background-color: rgb(235, 236, 238); +} + +QHeaderView, +QListView, +QListView::item, +QMenuBar, +QScrollArea, +QScrollArea > QWidget > QWidget, +QTreeWidget { + background-color: transparent; +} + +.QFrame, +QGroupBox, +QTabWidget::pane, +#SendCoinsEntry { + background: white; + border: none; + border-radius: 0.26em; + padding: 0.75em; +} + +/* HLine */ +QFrame[frameShape="4"] { + border-top: 0.065em solid rgb(234, 237, 237); +} + +/* VLine */ +QFrame[frameShape="5"] { + border-left: 0.065em solid rgb(234, 237, 237); } QToolBar#toolbar QToolButton { @@ -30,157 +66,191 @@ QToolBar#toolbar QToolButton:checked { background-color: rgb(200,200,200); } -QToolBar#toolbar QToolButton:checked:hover { - background-color: rgb(65,0,127); +QMenu { + background-color: white; + border: 0.065em solid rgb(224, 227, 227); } -QMenuBar { - background: rgb(240,240,240); - color: black; +QMenu::item { + padding: 0.25em 1.5em 0.25em 1.5em; } -QMenuBar::item { - padding-bottom:0.5em; - padding-top:0.5em; - padding-left:1em; - padding-right:1em; - color: black; - background-color: rgb(240,240,240); +QMenu::item:selected { + border: none; } -QMenuBar::item:selected { - background-color:rgb(65,0,127); - color: white; +QMenu::icon { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + margin-left: 0.5em; } -QMenu { - background: rgb(240,240,240); - color: black; - padding-bottom:0.625; +QMenu::separator { + background: none; + border-top: 0.065em solid rgb(234, 237, 237); + height: 0.065em; + margin: 0.13em 0 0.065em 0; } -QMenu::item { - color: black; - background-color: transparent; +QMenuBar { + spacing: 0; } -QMenu::item:selected { - background-color:rgb(65,0,127); - color:white; +QMenuBar::item { + margin: 0; + padding: 0.25em 1em; + border: none; } -/* horizontal scrollbar */ - -QScrollBar:horizontal { - background: rgb(240,240,240); - height: 0.625em; - margin: 0 0 0 0; +QMenu::item:disabled, +QMenuBar::item:disabled { + color: rgb(140, 140, 140); } -QScrollBar::handle:horizontal { - background: rgb(180,180,180); - border: 0.065em rgb(180,180,180); - border-radius: 0.19em; - min-width: 1.25wm; +QScrollArea { + border: none; } -QScrollBar::handle:horizontal:hover { - background: rgb(65,0,127); - border: 0.065em rgb(65,0,127); - border-radius: 0.19em; - min-width: 1.25em; +QScrollBar:horizontal, +QScrollBar:vertical { + background: rgb(245, 246, 248); + margin: 0; + border-radius: 0.26em; } -QScrollBar::add-line:horizontal { - border: none; - background-color: rgb(240,240,240); - width: 0; - height: 0; +QScrollBar:horizontal { + height: 0.5em; } -QScrollBar::sub-line:horizontal { - border: none; - background-color: rgb(240,240,240); - width: 0; - height: 0; +QScrollBar:vertical { + width: 0.5em; } -/* vertical scrollbar */ +QScrollBar::handle:horizontal, +QScrollBar::handle:vertical { + background: rgb(210, 210, 211); + border: none; + border-radius: 0.26em; +} -QScrollBar:vertical { - background: rgb(240,240,240); - width: 0.625em; - margin: 0 0 0 0; +QScrollBar::handle:horizontal { + min-width: 1.5em; } QScrollBar::handle:vertical { - background: rgb(180,180,180); - border: 0.065em rgb(180,180,180); - border-radius: 0.19em; - min-height: 1.25em; + min-height: 1.5em; } +QScrollBar::handle:horizontal:hover, QScrollBar::handle:vertical:hover { - background: rgb(65,0,127); - border: 0.065em rgb(65,0,127); - border-radius: 0.19em; - min-height: 1.25em; + background: rgb(180, 180, 180); } -QScrollBar::add-line:vertical { - border: none; - background-color: rgb(240,240,240); - width: 0; - height: 0; +QScrollBar::handle:horizontal:pressed, +QScrollBar::handle:vertical:pressed { + background: rgb(160, 160, 160); } +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal, +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + background: none; +} + +QScrollBar::add-line:horizontal, +QScrollBar::sub-line:horizontal, +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { border: none; - background-color: rgb(240,240,240); + background: transparent; width: 0; height: 0; } +QSplitter::handle { + background: rgba(224, 227, 227, 0.5); +} + +QSplitter::handle:hover, +QSplitter::handle:pressed { + background: rgb(224, 227, 227); +} + QToolTip { color: black; background-color: rgb(245,245,245); border: none; } +QGroupBox { + padding-top: 1.5em; +} -QTableView{ - background-color: rgb(240,240,240); - color:black; - alternate-background-color:white; +QGroupBox::title { + margin: 0.75em; + font-weight: bold; } -QTableView::item{ +QListWidget, +QTableView, +QTreeWidget { + background-color: white; + border: 0.065em solid rgb(194, 199, 205); + border-radius: 0.26em; +} + +QListWidget::item, +QTableView::item { background-color: transparent; - color:black; + border: none; } -QTableView::item:selected{ - background-color: rgb(65,0,127); - color:white; +QListWidget::item { + height: 1.5em; + padding-left: 0.5em; + padding-right: 0.5em; } -QTableView::item:hover{ - background-color: rgb(65,0,127); - color:white; +QListWidget QScrollBar:horizontal, +QTableView QScrollBar:horizontal, +QTreeWidget QScrollBar:horizontal { + border-top: 0.065em solid rgb(194, 199, 205); + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +QListWidget QScrollBar:vertical, +QTableView QScrollBar:vertical, +QTreeWidget QScrollBar:vertical { + border-left: 0.065em solid rgb(194, 199, 205); + border-top-left-radius: 0; + border-bottom-left-radius: 0; } -QHeaderView{ - background-color: rgb(220,220,220); +QHeaderView::section, +QTableView QTableCornerButton::section { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 245, 249)); + border: none; + border-bottom: 0.065em solid rgb(194, 199, 205); + padding: 0.25em; + color: rgb(58, 70, 94); } -QHeaderView::section { - background-color:rgb(220,220,220); - color:black; +QHeaderView::section:first, +QTableView QTableCornerButton::section { + border-top-left-radius: 0.26em; +} + +QHeaderView::section:last { + border-top-right-radius: 0.26em; } QHeaderView::section:hover { - background-color:rgb(65,0,127); - color:white; + color: black; } QComboBox, @@ -233,7 +303,7 @@ QSpinBox:disabled, QTextEdit:disabled, QToolButton:disabled { background-color: rgb(240, 240, 240); - color: rgb(140, 140, 140); + color: rgb(130, 130, 130); } QComboBox, @@ -277,41 +347,23 @@ QComboBox:selected { } QComboBox QAbstractItemView { - background-color:white; - color: rgb(88, 92, 107); + background-color: white; + color: rgb(70, 73, 85); } -QAbstractItemView::item:selected { - background-color:rgb(65,0,127); - color: white; +QAbstractItemView { + alternate-background-color: rgb(248, 248, 250); } QAbstractItemView::item:checked, QAbstractItemView::item:hover, -QAbstractItemView::item:selected { +QAbstractItemView::item:selected, +QMenu::item:selected, +QMenuBar::item:selected { background: rgb(241, 245, 247); color: black; } -QDateTimeEdit QAbstractItemView { - background-color:white; - color: black; -} - -QDateTimeEdit QAbstractItemView::item:checked { - background-color:rgb(65,0,127); - color: white; -} - -QDateTimeEdit QAbstractItemView::item:selected { - background-color:rgb(65,0,127); - color: white; -} - -QDialog{ - background-color: rgb(240,240,240); -} - QPushButton { padding: 0.2em 1.25em; color: rgb(97, 101, 118); @@ -322,32 +374,6 @@ QToolButton { color: rgb(97, 101, 118); } -QTreeWidget{ - color: rgb(0,0,0); - background-color: rgb(240,240,240); - alternate-background-color: white; -} - -QListView { - background: rgb(240,240,240); - alternate-background-color: white; -} - -QListView::item { - background-color:transparent; - color:black; -} - -QListView::item:selected { - background-color:rgb(65,0,127); - color:white; -} - -QListView::item:checked { - background-color:rgb(65,0,127); - color:white; -} - QDateEdit::up-button, QDateEdit::down-button, QDoubleSpinBox::up-button, @@ -388,37 +414,28 @@ QTabWidget::tab-bar { left: 0.5em; } -QTabWidget::pane { /* The tab widget frame */ - background-color: rgb(255, 255, 255); - border: 0.065em solid rgb(200, 200, 200); +QTabBar { + color: rgb(113, 121, 140); + font-weight: 500; } QTabBar::tab { - background-color: rgb(240, 240, 240); - border: 0.065em solid rgb(200, 200, 200); - border-bottom: none; + background-color: transparent; + border-bottom: 0.195em solid transparent; min-height: 1.25em; min-width: 0.5em; - padding: 0.125em 1em; -} - -QTabBar::tab:!first { - margin-left: -0.065em; + padding: 0.5em 0; + margin-bottom: 0.5em; + margin-right: 1.5em; } QTabBar::tab:selected { - background-color: rgb(255, 255, 255); - border-bottom-color: rgb(255, 255, 255); - border-top-color: rgb(120, 20, 255); - border-top-width: 0.13em; -} - -QTabBar::tab:!selected { - margin-top: 0.13em; + border-bottom-color: rgb(120, 20, 255); + color: rgb(120, 20, 255); } QTabBar::tab:!selected:hover { - background: rgb(255, 255, 255); + border-bottom-color: rgb(113, 121, 140); } /* RPC Console */ @@ -426,15 +443,6 @@ QTabBar::tab:!selected:hover { font-family: "Inconsolata"; } -QGroupBox{ - background-color: rgb(240,240,240); -} - -QListWidget{ - color:black; - background-color:rgb(240,240,240); -} - /* Main Window*/ #toolbar { @@ -467,6 +475,10 @@ QToolBar#toolbar QToolButton { width:100%; } +QToolBar#toolbar2 .QFrame { + padding: 0; +} + QToolBar#toolbar2 QLabel{ border:none; padding-top:1em; @@ -517,8 +529,9 @@ QToolBar#toolbar3 QLabel{ #researcherHeaderLabel, #stakingHeaderLabel, #recentTransLabel { - font-size:15pt; - font-weight:bold; + font-size: 15pt; + font-weight: bold; + color: rgb(43, 52, 69); } #availableLabel, diff --git a/src/qt/res/stylesheets/native_stylesheet.qss b/src/qt/res/stylesheets/native_stylesheet.qss index 9eefeba001..d38d60df3a 100644 --- a/src/qt/res/stylesheets/native_stylesheet.qss +++ b/src/qt/res/stylesheets/native_stylesheet.qss @@ -14,10 +14,11 @@ QMenuBar { } QMenuBar::item { - padding-bottom:0.5em; - padding-top:0.5em; - padding-left:1em; - padding-right:1em; + border: 0; + padding-bottom: 0.25em; + padding-top: 0.25em; + padding-left: 1em; + padding-right: 1em; color: black; background-color: rgb(240,240,240); } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index fa2b9c9abb..a5dcadd35f 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -116,6 +116,7 @@ TransactionView::TransactionView(QWidget *parent) : view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); view->setTabKeyNavigation(false); view->setContextMenuPolicy(Qt::CustomContextMenu); + view->setShowGrid(false); transactionView = view; diff --git a/src/qt/votingdialog.cpp b/src/qt/votingdialog.cpp index 7c86da59b2..628adc2961 100644 --- a/src/qt/votingdialog.cpp +++ b/src/qt/votingdialog.cpp @@ -422,6 +422,7 @@ VotingDialog::VotingDialog(QWidget *parent) tableView_->setSortingEnabled(true); tableView_->sortByColumn(VotingTableModel::RowNumber, Qt::DescendingOrder); tableView_->verticalHeader()->hide(); + tableView_->setShowGrid(false); tableView_->setModel(proxyModel_); tableView_->setFont(QFont("Arial", 10)); From b067565ae0868e986b85da49266acf04574fe141 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:32 -0500 Subject: [PATCH 032/280] Install new sidebar and status bar icons This adds sidebar and status bar icons from the proposed redesign of the GUI. --- src/Makefile.qt.include | 97 ++++++++-- src/qt/bitcoin.qrc | 167 ++++++++++++++---- .../icons_dark/sidebar_favorites_active.svg | 1 + .../icons_dark/sidebar_favorites_inactive.svg | 1 + .../icons_dark/sidebar_history_active.svg | 1 + .../icons_dark/sidebar_history_inactive.svg | 1 + .../icons_dark/sidebar_locked_active.svg | 1 + .../icons_dark/sidebar_locked_inactive.svg | 1 + .../icons_dark/sidebar_overview_active.svg | 1 + .../icons_dark/sidebar_overview_inactive.svg | 1 + .../icons_dark/sidebar_receive_active.svg | 1 + .../icons_dark/sidebar_receive_inactive.svg | 1 + .../icons/icons_dark/sidebar_send_active.svg | 1 + .../icons_dark/sidebar_send_inactive.svg | 1 + .../icons_dark/sidebar_unlocked_active.svg | 1 + .../icons_dark/sidebar_unlocked_inactive.svg | 1 + .../icons_dark/sidebar_voting_active.svg | 1 + .../icons_dark/sidebar_voting_inactive.svg | 1 + .../icons/icons_dark/status_beacon_gray.svg | 7 + .../icons/icons_dark/status_beacon_green.svg | 14 ++ .../icons/icons_dark/status_beacon_red.svg | 7 + .../icons/icons_dark/status_beacon_yellow.svg | 14 ++ .../icons_dark/status_connections_average.svg | 1 + .../icons_dark/status_connections_good.svg | 1 + .../icons_dark/status_connections_none.svg | 1 + .../icons_dark/status_connections_normal.svg | 1 + .../icons_dark/status_connections_poor.svg | 1 + .../icons_dark/status_encryption_locked.svg | 1 + .../icons_dark/status_encryption_none.svg | 1 + .../icons_dark/status_encryption_unlocked.svg | 1 + .../icons_dark/status_scraper_inactive.svg | 3 + .../status_scraper_no_convergence.svg | 3 + .../icons/icons_dark/status_scraper_ok.svg | 3 + .../icons_dark/status_scraper_waiting.svg | 3 + .../icons/icons_dark/status_staking_no.svg | 1 + .../icons_dark/status_staking_problem.svg | 1 + .../icons/icons_dark/status_staking_yes.svg | 1 + .../res/icons/icons_dark/status_sync_done.svg | 1 + .../icons/icons_dark/status_sync_stalled.svg | 1 + .../icons/icons_dark/status_sync_syncing.svg | 1 + .../icons_light/sidebar_favorites_active.svg | 1 + .../sidebar_favorites_inactive.svg | 1 + .../icons_light/sidebar_history_active.svg | 1 + .../icons_light/sidebar_history_inactive.svg | 1 + .../icons_light/sidebar_locked_active.svg | 1 + .../icons_light/sidebar_locked_inactive.svg | 1 + .../icons_light/sidebar_overview_active.svg | 1 + .../icons_light/sidebar_overview_inactive.svg | 1 + .../icons_light/sidebar_receive_active.svg | 1 + .../icons_light/sidebar_receive_inactive.svg | 1 + .../icons/icons_light/sidebar_send_active.svg | 1 + .../icons_light/sidebar_send_inactive.svg | 1 + .../icons_light/sidebar_unlocked_active.svg | 1 + .../icons_light/sidebar_unlocked_inactive.svg | 1 + .../icons_light/sidebar_voting_active.svg | 1 + .../icons_light/sidebar_voting_inactive.svg | 1 + .../icons/icons_light/status_beacon_gray.svg | 7 + .../icons/icons_light/status_beacon_green.svg | 14 ++ .../icons/icons_light/status_beacon_red.svg | 7 + .../icons_light/status_beacon_yellow.svg | 14 ++ .../status_connections_average.svg | 1 + .../icons_light/status_connections_good.svg | 1 + .../icons_light/status_connections_none.svg | 1 + .../icons_light/status_connections_normal.svg | 1 + .../icons_light/status_connections_poor.svg | 1 + .../icons_light/status_encryption_locked.svg | 1 + .../icons_light/status_encryption_none.svg | 1 + .../status_encryption_unlocked.svg | 1 + .../icons_light/status_scraper_inactive.svg | 3 + .../status_scraper_no_convergence.svg | 3 + .../icons/icons_light/status_scraper_ok.svg | 3 + .../icons_light/status_scraper_waiting.svg | 3 + .../icons/icons_light/status_staking_no.svg | 1 + .../icons_light/status_staking_problem.svg | 1 + .../icons/icons_light/status_staking_yes.svg | 1 + .../icons/icons_light/status_sync_done.svg | 1 + .../icons/icons_light/status_sync_stalled.svg | 1 + .../icons/icons_light/status_sync_syncing.svg | 1 + src/qt/res/icons/transaction2.png | Bin 211 -> 0 bytes src/qt/res/icons/transaction2.svg | 4 + src/qt/res/images/gridcoin.svg | 5 +- 81 files changed, 382 insertions(+), 59 deletions(-) create mode 100644 src/qt/res/icons/icons_dark/sidebar_favorites_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_favorites_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_history_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_history_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_locked_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_locked_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_overview_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_overview_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_receive_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_receive_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_send_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_send_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_unlocked_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_voting_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_voting_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/status_beacon_gray.svg create mode 100644 src/qt/res/icons/icons_dark/status_beacon_green.svg create mode 100644 src/qt/res/icons/icons_dark/status_beacon_red.svg create mode 100644 src/qt/res/icons/icons_dark/status_beacon_yellow.svg create mode 100644 src/qt/res/icons/icons_dark/status_connections_average.svg create mode 100644 src/qt/res/icons/icons_dark/status_connections_good.svg create mode 100644 src/qt/res/icons/icons_dark/status_connections_none.svg create mode 100644 src/qt/res/icons/icons_dark/status_connections_normal.svg create mode 100644 src/qt/res/icons/icons_dark/status_connections_poor.svg create mode 100644 src/qt/res/icons/icons_dark/status_encryption_locked.svg create mode 100644 src/qt/res/icons/icons_dark/status_encryption_none.svg create mode 100644 src/qt/res/icons/icons_dark/status_encryption_unlocked.svg create mode 100644 src/qt/res/icons/icons_dark/status_scraper_inactive.svg create mode 100644 src/qt/res/icons/icons_dark/status_scraper_no_convergence.svg create mode 100644 src/qt/res/icons/icons_dark/status_scraper_ok.svg create mode 100644 src/qt/res/icons/icons_dark/status_scraper_waiting.svg create mode 100644 src/qt/res/icons/icons_dark/status_staking_no.svg create mode 100644 src/qt/res/icons/icons_dark/status_staking_problem.svg create mode 100644 src/qt/res/icons/icons_dark/status_staking_yes.svg create mode 100644 src/qt/res/icons/icons_dark/status_sync_done.svg create mode 100644 src/qt/res/icons/icons_dark/status_sync_stalled.svg create mode 100644 src/qt/res/icons/icons_dark/status_sync_syncing.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_favorites_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_favorites_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_history_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_history_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_locked_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_locked_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_overview_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_overview_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_receive_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_receive_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_send_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_send_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_unlocked_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_unlocked_inactive.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_voting_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_voting_inactive.svg create mode 100644 src/qt/res/icons/icons_light/status_beacon_gray.svg create mode 100644 src/qt/res/icons/icons_light/status_beacon_green.svg create mode 100644 src/qt/res/icons/icons_light/status_beacon_red.svg create mode 100644 src/qt/res/icons/icons_light/status_beacon_yellow.svg create mode 100644 src/qt/res/icons/icons_light/status_connections_average.svg create mode 100644 src/qt/res/icons/icons_light/status_connections_good.svg create mode 100644 src/qt/res/icons/icons_light/status_connections_none.svg create mode 100644 src/qt/res/icons/icons_light/status_connections_normal.svg create mode 100644 src/qt/res/icons/icons_light/status_connections_poor.svg create mode 100644 src/qt/res/icons/icons_light/status_encryption_locked.svg create mode 100644 src/qt/res/icons/icons_light/status_encryption_none.svg create mode 100644 src/qt/res/icons/icons_light/status_encryption_unlocked.svg create mode 100644 src/qt/res/icons/icons_light/status_scraper_inactive.svg create mode 100644 src/qt/res/icons/icons_light/status_scraper_no_convergence.svg create mode 100644 src/qt/res/icons/icons_light/status_scraper_ok.svg create mode 100644 src/qt/res/icons/icons_light/status_scraper_waiting.svg create mode 100644 src/qt/res/icons/icons_light/status_staking_no.svg create mode 100644 src/qt/res/icons/icons_light/status_staking_problem.svg create mode 100644 src/qt/res/icons/icons_light/status_staking_yes.svg create mode 100644 src/qt/res/icons/icons_light/status_sync_done.svg create mode 100644 src/qt/res/icons/icons_light/status_sync_stalled.svg create mode 100644 src/qt/res/icons/icons_light/status_sync_syncing.svg delete mode 100644 src/qt/res/icons/transaction2.png create mode 100644 src/qt/res/icons/transaction2.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c7cdfacf6b..640cbfb740 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -287,10 +287,6 @@ GRIDCOINRESEARCH_QT_CPP = \ RES_ICONS = \ qt/res/icons/add.png \ - qt/res/icons/beacon_green.svg \ - qt/res/icons/beacon_grey.svg \ - qt/res/icons/beacon_red.svg \ - qt/res/icons/beacon_yellow.svg \ qt/res/icons/block.png \ qt/res/icons/chat.png \ qt/res/icons/clock1.png \ @@ -330,7 +326,8 @@ RES_ICONS = \ qt/res/icons/superblock.svg \ qt/res/icons/transaction_conflicted.png \ qt/res/icons/transaction0.png \ - qt/res/icons/transaction2.png \ + qt/res/icons/transaction2.svg \ + qt/res/icons/tx_contract_voting.svg \ qt/res/icons/tx_inout.svg \ qt/res/icons/tx_input.svg \ qt/res/icons/tx_output.svg \ @@ -344,26 +341,88 @@ RES_ICONS = \ qt/res/icons/white_and_red_x.svg \ qt/res/icons/www.png \ qt/res/icons/icons_native/overview.svg \ - qt/res/icons/icons_light/overview.svg \ - qt/res/icons/icons_dark/overview.svg \ qt/res/icons/icons_native/Voting.svg \ qt/res/icons/icons_native/address-book.svg \ qt/res/icons/icons_native/lock_closed.svg \ qt/res/icons/icons_native/lock_open.svg \ qt/res/icons/icons_native/receive.svg \ qt/res/icons/icons_native/send.svg \ - qt/res/icons/icons_light/Voting.svg \ - qt/res/icons/icons_light/address-book.svg \ - qt/res/icons/icons_light/lock_closed.svg \ - qt/res/icons/icons_light/lock_open.svg \ - qt/res/icons/icons_light/receive.svg \ - qt/res/icons/icons_light/send.svg \ - qt/res/icons/icons_dark/Voting.svg \ - qt/res/icons/icons_dark/address-book.svg \ - qt/res/icons/icons_dark/lock_closed.svg \ - qt/res/icons/icons_dark/lock_open.svg \ - qt/res/icons/icons_dark/receive.svg \ - qt/res/icons/icons_dark/send.svg \ + qt/res/icons/icons_light/sidebar_favorites_active.svg \ + qt/res/icons/icons_light/sidebar_favorites_inactive.svg \ + qt/res/icons/icons_light/sidebar_history_active.svg \ + qt/res/icons/icons_light/sidebar_history_inactive.svg \ + qt/res/icons/icons_light/sidebar_locked_active.svg \ + qt/res/icons/icons_light/sidebar_locked_inactive.svg \ + qt/res/icons/icons_light/sidebar_overview_active.svg \ + qt/res/icons/icons_light/sidebar_overview_inactive.svg \ + qt/res/icons/icons_light/sidebar_receive_active.svg \ + qt/res/icons/icons_light/sidebar_receive_inactive.svg \ + qt/res/icons/icons_light/sidebar_send_active.svg \ + qt/res/icons/icons_light/sidebar_send_inactive.svg \ + qt/res/icons/icons_light/sidebar_unlocked_active.svg \ + qt/res/icons/icons_light/sidebar_unlocked_inactive.svg \ + qt/res/icons/icons_light/sidebar_voting_active.svg \ + qt/res/icons/icons_light/sidebar_voting_inactive.svg \ + qt/res/icons/icons_light/status_beacon_green.svg \ + qt/res/icons/icons_light/status_beacon_gray.svg \ + qt/res/icons/icons_light/status_beacon_red.svg \ + qt/res/icons/icons_light/status_beacon_yellow.svg \ + qt/res/icons/icons_light/status_connections_average.svg \ + qt/res/icons/icons_light/status_connections_good.svg \ + qt/res/icons/icons_light/status_connections_none.svg \ + qt/res/icons/icons_light/status_connections_normal.svg \ + qt/res/icons/icons_light/status_connections_poor.svg \ + qt/res/icons/icons_light/status_encryption_none.svg \ + qt/res/icons/icons_light/status_encryption_locked.svg \ + qt/res/icons/icons_light/status_encryption_unlocked.svg \ + qt/res/icons/icons_light/status_scraper_inactive.svg \ + qt/res/icons/icons_light/status_scraper_no_convergence.svg \ + qt/res/icons/icons_light/status_scraper_ok.svg \ + qt/res/icons/icons_light/status_scraper_waiting.svg \ + qt/res/icons/icons_light/status_staking_no.svg \ + qt/res/icons/icons_light/status_staking_problem.svg \ + qt/res/icons/icons_light/status_staking_yes.svg \ + qt/res/icons/icons_light/status_sync_done.svg \ + qt/res/icons/icons_light/status_sync_stalled.svg \ + qt/res/icons/icons_light/status_sync_syncing.svg \ + qt/res/icons/icons_dark/sidebar_favorites_active.svg \ + qt/res/icons/icons_dark/sidebar_favorites_inactive.svg \ + qt/res/icons/icons_dark/sidebar_history_active.svg \ + qt/res/icons/icons_dark/sidebar_history_inactive.svg \ + qt/res/icons/icons_dark/sidebar_locked_active.svg \ + qt/res/icons/icons_dark/sidebar_locked_inactive.svg \ + qt/res/icons/icons_dark/sidebar_overview_active.svg \ + qt/res/icons/icons_dark/sidebar_overview_inactive.svg \ + qt/res/icons/icons_dark/sidebar_receive_active.svg \ + qt/res/icons/icons_dark/sidebar_receive_inactive.svg \ + qt/res/icons/icons_dark/sidebar_send_active.svg \ + qt/res/icons/icons_dark/sidebar_send_inactive.svg \ + qt/res/icons/icons_dark/sidebar_unlocked_active.svg \ + qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg \ + qt/res/icons/icons_dark/sidebar_voting_active.svg \ + qt/res/icons/icons_dark/sidebar_voting_inactive.svg \ + qt/res/icons/icons_dark/status_beacon_green.svg \ + qt/res/icons/icons_dark/status_beacon_gray.svg \ + qt/res/icons/icons_dark/status_beacon_red.svg \ + qt/res/icons/icons_dark/status_beacon_yellow.svg \ + qt/res/icons/icons_dark/status_connections_average.svg \ + qt/res/icons/icons_dark/status_connections_good.svg \ + qt/res/icons/icons_dark/status_connections_none.svg \ + qt/res/icons/icons_dark/status_connections_normal.svg \ + qt/res/icons/icons_dark/status_connections_poor.svg \ + qt/res/icons/icons_dark/status_encryption_none.svg \ + qt/res/icons/icons_dark/status_encryption_locked.svg \ + qt/res/icons/icons_dark/status_encryption_unlocked.svg \ + qt/res/icons/icons_dark/status_scraper_inactive.svg \ + qt/res/icons/icons_dark/status_scraper_no_convergence.svg \ + qt/res/icons/icons_dark/status_scraper_ok.svg \ + qt/res/icons/icons_dark/status_scraper_waiting.svg \ + qt/res/icons/icons_dark/status_staking_no.svg \ + qt/res/icons/icons_dark/status_staking_problem.svg \ + qt/res/icons/icons_dark/status_staking_yes.svg \ + qt/res/icons/icons_dark/status_sync_done.svg \ + qt/res/icons/icons_dark/status_sync_stalled.svg \ + qt/res/icons/icons_dark/status_sync_syncing.svg \ qt/res/icons/icons_dark/transactions.svg \ qt/res/icons/icons_light/transactions.svg \ qt/res/icons/icons_native/transactions.svg \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 8aa26536db..663c8f557c 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -2,7 +2,7 @@ res/icons/quit.png res/icons/transaction0.png - res/icons/transaction2.png + res/icons/transaction2.svg res/icons/transaction_conflicted.png res/icons/clock1.png res/icons/clock2.png @@ -28,50 +28,146 @@ res/icons/tx_input.svg res/icons/tx_pos.svg res/icons/tx_output.svg - res/icons/icons_native/overview.svg - res/icons/icons_light/overview.svg - res/icons/icons_dark/overview.svg - res/icons/icons_native/Voting.svg + + + + + res/icons/icons_light/sidebar_favorites_inactive.svg + res/icons/icons_light/sidebar_favorites_active.svg + res/icons/icons_light/sidebar_history_inactive.svg + res/icons/icons_light/sidebar_history_active.svg + res/icons/icons_light/sidebar_locked_inactive.svg + res/icons/icons_light/sidebar_locked_active.svg + res/icons/icons_light/sidebar_unlocked_inactive.svg + res/icons/icons_light/sidebar_unlocked_active.svg + res/icons/icons_light/sidebar_overview_inactive.svg + res/icons/icons_light/sidebar_overview_active.svg + res/icons/icons_light/sidebar_receive_inactive.svg + res/icons/icons_light/sidebar_receive_active.svg + res/icons/icons_light/sidebar_send_inactive.svg + res/icons/icons_light/sidebar_send_active.svg + res/icons/icons_light/sidebar_voting_inactive.svg + res/icons/icons_light/sidebar_voting_active.svg + + + res/icons/icons_light/status_beacon_green.svg + res/icons/icons_light/status_beacon_gray.svg + res/icons/icons_light/status_beacon_red.svg + res/icons/icons_light/status_beacon_yellow.svg + + + res/icons/icons_light/status_sync_done.svg + res/icons/icons_light/status_sync_stalled.svg + res/icons/icons_light/status_sync_syncing.svg + + + res/icons/icons_light/status_staking_no.svg + res/icons/icons_light/status_staking_problem.svg + res/icons/icons_light/status_staking_yes.svg + + + res/icons/icons_light/status_connections_none.svg + res/icons/icons_light/status_connections_poor.svg + res/icons/icons_light/status_connections_average.svg + res/icons/icons_light/status_connections_normal.svg + res/icons/icons_light/status_connections_good.svg + + + res/icons/icons_light/status_scraper_inactive.svg + res/icons/icons_light/status_scraper_no_convergence.svg + res/icons/icons_light/status_scraper_ok.svg + res/icons/icons_light/status_scraper_waiting.svg + + + res/icons/icons_light/status_encryption_locked.svg + res/icons/icons_light/status_encryption_none.svg + res/icons/icons_light/status_encryption_unlocked.svg + + + res/icons/icons_light/chevron_down.svg + res/icons/icons_light/chevron_up.svg + + + + + res/icons/icons_dark/sidebar_favorites_inactive.svg + res/icons/icons_dark/sidebar_favorites_active.svg + res/icons/icons_dark/sidebar_history_inactive.svg + res/icons/icons_dark/sidebar_history_active.svg + res/icons/icons_dark/sidebar_locked_inactive.svg + res/icons/icons_dark/sidebar_locked_active.svg + res/icons/icons_dark/sidebar_unlocked_inactive.svg + res/icons/icons_dark/sidebar_unlocked_active.svg + res/icons/icons_dark/sidebar_overview_inactive.svg + res/icons/icons_dark/sidebar_overview_active.svg + res/icons/icons_dark/sidebar_receive_inactive.svg + res/icons/icons_dark/sidebar_receive_active.svg + res/icons/icons_dark/sidebar_send_inactive.svg + res/icons/icons_dark/sidebar_send_active.svg + res/icons/icons_dark/sidebar_voting_inactive.svg + res/icons/icons_dark/sidebar_voting_active.svg + + + res/icons/icons_dark/status_beacon_green.svg + res/icons/icons_dark/status_beacon_gray.svg + res/icons/icons_dark/status_beacon_red.svg + res/icons/icons_dark/status_beacon_yellow.svg + + + res/icons/icons_dark/status_sync_done.svg + res/icons/icons_dark/status_sync_stalled.svg + res/icons/icons_dark/status_sync_syncing.svg + + + res/icons/icons_dark/status_staking_no.svg + res/icons/icons_dark/status_staking_problem.svg + res/icons/icons_dark/status_staking_yes.svg + + + res/icons/icons_dark/status_connections_none.svg + res/icons/icons_dark/status_connections_poor.svg + res/icons/icons_dark/status_connections_average.svg + res/icons/icons_dark/status_connections_normal.svg + res/icons/icons_dark/status_connections_good.svg + + + res/icons/icons_dark/status_scraper_inactive.svg + res/icons/icons_dark/status_scraper_no_convergence.svg + res/icons/icons_dark/status_scraper_ok.svg + res/icons/icons_dark/status_scraper_waiting.svg + + + res/icons/icons_dark/status_encryption_locked.svg + res/icons/icons_dark/status_encryption_none.svg + res/icons/icons_dark/status_encryption_unlocked.svg + + + res/icons/icons_dark/chevron_down.svg + res/icons/icons_dark/chevron_up.svg + + + res/icons/icons_native/address-book.svg + res/icons/icons_native/transactions.svg res/icons/icons_native/lock_closed.svg res/icons/icons_native/lock_open.svg + res/icons/icons_native/overview.svg res/icons/icons_native/receive.svg res/icons/icons_native/send.svg - res/icons/icons_light/Voting.svg - res/icons/icons_light/address-book.svg - res/icons/icons_light/lock_closed.svg - res/icons/icons_light/lock_open.svg - res/icons/icons_light/receive.svg - res/icons/icons_light/send.svg - res/icons/icons_dark/Voting.svg - res/icons/icons_dark/address-book.svg - res/icons/icons_dark/lock_closed.svg - res/icons/icons_dark/lock_open.svg - res/icons/icons_dark/receive.svg - res/icons/icons_dark/send.svg - res/icons/icons_dark/transactions.svg - res/icons/icons_light/transactions.svg - res/icons/icons_native/transactions.svg + res/icons/icons_native/Voting.svg + res/icons/tx_pos_ss.svg res/icons/tx_por.svg res/icons/tx_por_ss.svg - res/icons/beacon_green.svg - res/icons/beacon_grey.svg - res/icons/beacon_red.svg - res/icons/beacon_yellow.svg - res/icons/staking_off.svg - res/icons/staking_on.svg res/icons/gray_scraper.svg res/icons/green_scraper.svg - res/icons/connect0.svg - res/icons/connect1.svg - res/icons/connect2.svg - res/icons/connect3.svg - res/icons/connect4.svg - res/icons/notsynced.svg - res/icons/green_check.svg res/icons/white_and_red_x.svg - res/icons/staking_unable.svg res/icons/superblock.svg res/icons/warning.svg res/icons/round_green_check.svg @@ -79,9 +175,6 @@ res/icons/tx_pos_ss_sent.svg res/icons/tx_por_ss_sent.svg res/icons/message.svg - res/icons/icons_light/chevron_down.svg - res/icons/icons_light/chevron_up.svg - res/icons/icons_dark/chevron_down.svg res/images/splash3.png diff --git a/src/qt/res/icons/icons_dark/sidebar_favorites_active.svg b/src/qt/res/icons/icons_dark/sidebar_favorites_active.svg new file mode 100644 index 0000000000..fb9f0509c1 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_favorites_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_favorites_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_favorites_inactive.svg new file mode 100644 index 0000000000..97193305e7 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_favorites_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_history_active.svg b/src/qt/res/icons/icons_dark/sidebar_history_active.svg new file mode 100644 index 0000000000..a2e1f932f1 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_history_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_history_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_history_inactive.svg new file mode 100644 index 0000000000..c95f70a137 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_history_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_locked_active.svg b/src/qt/res/icons/icons_dark/sidebar_locked_active.svg new file mode 100644 index 0000000000..c131b6f61e --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_locked_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_locked_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_locked_inactive.svg new file mode 100644 index 0000000000..0c66323d1b --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_locked_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_overview_active.svg b/src/qt/res/icons/icons_dark/sidebar_overview_active.svg new file mode 100644 index 0000000000..0b33cd3244 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_overview_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_overview_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_overview_inactive.svg new file mode 100644 index 0000000000..abdfb03bd6 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_overview_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_receive_active.svg b/src/qt/res/icons/icons_dark/sidebar_receive_active.svg new file mode 100644 index 0000000000..1eeb2fc6bc --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_receive_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_receive_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_receive_inactive.svg new file mode 100644 index 0000000000..fcf9ba8719 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_receive_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_send_active.svg b/src/qt/res/icons/icons_dark/sidebar_send_active.svg new file mode 100644 index 0000000000..65e15adc3e --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_send_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_send_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_send_inactive.svg new file mode 100644 index 0000000000..d9513289ac --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_send_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_unlocked_active.svg b/src/qt/res/icons/icons_dark/sidebar_unlocked_active.svg new file mode 100644 index 0000000000..ab79a7a091 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_unlocked_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg new file mode 100644 index 0000000000..ad565cad36 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_voting_active.svg b/src/qt/res/icons/icons_dark/sidebar_voting_active.svg new file mode 100644 index 0000000000..b0b4bf419e --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_voting_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_voting_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_voting_inactive.svg new file mode 100644 index 0000000000..7c1c411cef --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_voting_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_beacon_gray.svg b/src/qt/res/icons/icons_dark/status_beacon_gray.svg new file mode 100644 index 0000000000..56a3279ff5 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_beacon_gray.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/qt/res/icons/icons_dark/status_beacon_green.svg b/src/qt/res/icons/icons_dark/status_beacon_green.svg new file mode 100644 index 0000000000..f7bd7de822 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_beacon_green.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/icons_dark/status_beacon_red.svg b/src/qt/res/icons/icons_dark/status_beacon_red.svg new file mode 100644 index 0000000000..3e95687c37 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_beacon_red.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/qt/res/icons/icons_dark/status_beacon_yellow.svg b/src/qt/res/icons/icons_dark/status_beacon_yellow.svg new file mode 100644 index 0000000000..d5dd341cc4 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_beacon_yellow.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/icons_dark/status_connections_average.svg b/src/qt/res/icons/icons_dark/status_connections_average.svg new file mode 100644 index 0000000000..10bed710fe --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_connections_average.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_connections_good.svg b/src/qt/res/icons/icons_dark/status_connections_good.svg new file mode 100644 index 0000000000..6c26b912b0 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_connections_good.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_connections_none.svg b/src/qt/res/icons/icons_dark/status_connections_none.svg new file mode 100644 index 0000000000..a2b31b53c2 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_connections_none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_connections_normal.svg b/src/qt/res/icons/icons_dark/status_connections_normal.svg new file mode 100644 index 0000000000..5561b4b717 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_connections_normal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_connections_poor.svg b/src/qt/res/icons/icons_dark/status_connections_poor.svg new file mode 100644 index 0000000000..b0a9d8418c --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_connections_poor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_encryption_locked.svg b/src/qt/res/icons/icons_dark/status_encryption_locked.svg new file mode 100644 index 0000000000..07520365d5 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_encryption_locked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_encryption_none.svg b/src/qt/res/icons/icons_dark/status_encryption_none.svg new file mode 100644 index 0000000000..fdc84cb50a --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_encryption_none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_encryption_unlocked.svg b/src/qt/res/icons/icons_dark/status_encryption_unlocked.svg new file mode 100644 index 0000000000..deb789895a --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_encryption_unlocked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_scraper_inactive.svg b/src/qt/res/icons/icons_dark/status_scraper_inactive.svg new file mode 100644 index 0000000000..eed7a104e9 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_scraper_inactive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_dark/status_scraper_no_convergence.svg b/src/qt/res/icons/icons_dark/status_scraper_no_convergence.svg new file mode 100644 index 0000000000..0f2a2742e7 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_scraper_no_convergence.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_dark/status_scraper_ok.svg b/src/qt/res/icons/icons_dark/status_scraper_ok.svg new file mode 100644 index 0000000000..68d43aa5c7 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_scraper_ok.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_dark/status_scraper_waiting.svg b/src/qt/res/icons/icons_dark/status_scraper_waiting.svg new file mode 100644 index 0000000000..ebf5438a43 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_scraper_waiting.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_dark/status_staking_no.svg b/src/qt/res/icons/icons_dark/status_staking_no.svg new file mode 100644 index 0000000000..11442fe376 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_staking_no.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_staking_problem.svg b/src/qt/res/icons/icons_dark/status_staking_problem.svg new file mode 100644 index 0000000000..c95102b923 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_staking_problem.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_dark/status_staking_yes.svg b/src/qt/res/icons/icons_dark/status_staking_yes.svg new file mode 100644 index 0000000000..fb44cceefe --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_staking_yes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_sync_done.svg b/src/qt/res/icons/icons_dark/status_sync_done.svg new file mode 100644 index 0000000000..9fbe197480 --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_sync_done.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/status_sync_stalled.svg b/src/qt/res/icons/icons_dark/status_sync_stalled.svg new file mode 100644 index 0000000000..1683352d6b --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_sync_stalled.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_dark/status_sync_syncing.svg b/src/qt/res/icons/icons_dark/status_sync_syncing.svg new file mode 100644 index 0000000000..c7cea578cf --- /dev/null +++ b/src/qt/res/icons/icons_dark/status_sync_syncing.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_favorites_active.svg b/src/qt/res/icons/icons_light/sidebar_favorites_active.svg new file mode 100644 index 0000000000..97193305e7 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_favorites_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_favorites_inactive.svg b/src/qt/res/icons/icons_light/sidebar_favorites_inactive.svg new file mode 100644 index 0000000000..d95ed661fc --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_favorites_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_history_active.svg b/src/qt/res/icons/icons_light/sidebar_history_active.svg new file mode 100644 index 0000000000..c95f70a137 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_history_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_history_inactive.svg b/src/qt/res/icons/icons_light/sidebar_history_inactive.svg new file mode 100644 index 0000000000..1a8f4fd122 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_history_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_locked_active.svg b/src/qt/res/icons/icons_light/sidebar_locked_active.svg new file mode 100644 index 0000000000..0c66323d1b --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_locked_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_locked_inactive.svg b/src/qt/res/icons/icons_light/sidebar_locked_inactive.svg new file mode 100644 index 0000000000..e99ab48924 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_locked_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_overview_active.svg b/src/qt/res/icons/icons_light/sidebar_overview_active.svg new file mode 100644 index 0000000000..abdfb03bd6 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_overview_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_overview_inactive.svg b/src/qt/res/icons/icons_light/sidebar_overview_inactive.svg new file mode 100644 index 0000000000..7a4a18dac7 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_overview_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_receive_active.svg b/src/qt/res/icons/icons_light/sidebar_receive_active.svg new file mode 100644 index 0000000000..fcf9ba8719 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_receive_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_receive_inactive.svg b/src/qt/res/icons/icons_light/sidebar_receive_inactive.svg new file mode 100644 index 0000000000..91fc2684c9 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_receive_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_send_active.svg b/src/qt/res/icons/icons_light/sidebar_send_active.svg new file mode 100644 index 0000000000..d9513289ac --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_send_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_send_inactive.svg b/src/qt/res/icons/icons_light/sidebar_send_inactive.svg new file mode 100644 index 0000000000..eaab28b289 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_send_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_unlocked_active.svg b/src/qt/res/icons/icons_light/sidebar_unlocked_active.svg new file mode 100644 index 0000000000..ad565cad36 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_unlocked_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_unlocked_inactive.svg b/src/qt/res/icons/icons_light/sidebar_unlocked_inactive.svg new file mode 100644 index 0000000000..f710854d0b --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_unlocked_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_voting_active.svg b/src/qt/res/icons/icons_light/sidebar_voting_active.svg new file mode 100644 index 0000000000..7c1c411cef --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_voting_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sidebar_voting_inactive.svg b/src/qt/res/icons/icons_light/sidebar_voting_inactive.svg new file mode 100644 index 0000000000..b3357b457d --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_voting_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/status_beacon_gray.svg b/src/qt/res/icons/icons_light/status_beacon_gray.svg new file mode 100644 index 0000000000..9e0979d8e6 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_beacon_gray.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_beacon_green.svg b/src/qt/res/icons/icons_light/status_beacon_green.svg new file mode 100644 index 0000000000..0f55289004 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_beacon_green.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_beacon_red.svg b/src/qt/res/icons/icons_light/status_beacon_red.svg new file mode 100644 index 0000000000..3e95687c37 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_beacon_red.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_beacon_yellow.svg b/src/qt/res/icons/icons_light/status_beacon_yellow.svg new file mode 100644 index 0000000000..cabd5152ad --- /dev/null +++ b/src/qt/res/icons/icons_light/status_beacon_yellow.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_connections_average.svg b/src/qt/res/icons/icons_light/status_connections_average.svg new file mode 100644 index 0000000000..83fdf77956 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_connections_average.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_connections_good.svg b/src/qt/res/icons/icons_light/status_connections_good.svg new file mode 100644 index 0000000000..a2b31b53c2 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_connections_good.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_connections_none.svg b/src/qt/res/icons/icons_light/status_connections_none.svg new file mode 100644 index 0000000000..09774569c4 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_connections_none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_connections_normal.svg b/src/qt/res/icons/icons_light/status_connections_normal.svg new file mode 100644 index 0000000000..e24b3f4e59 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_connections_normal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_connections_poor.svg b/src/qt/res/icons/icons_light/status_connections_poor.svg new file mode 100644 index 0000000000..212f44da62 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_connections_poor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_encryption_locked.svg b/src/qt/res/icons/icons_light/status_encryption_locked.svg new file mode 100644 index 0000000000..529b4a1f9a --- /dev/null +++ b/src/qt/res/icons/icons_light/status_encryption_locked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_encryption_none.svg b/src/qt/res/icons/icons_light/status_encryption_none.svg new file mode 100644 index 0000000000..fdc84cb50a --- /dev/null +++ b/src/qt/res/icons/icons_light/status_encryption_none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_encryption_unlocked.svg b/src/qt/res/icons/icons_light/status_encryption_unlocked.svg new file mode 100644 index 0000000000..1380174796 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_encryption_unlocked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_scraper_inactive.svg b/src/qt/res/icons/icons_light/status_scraper_inactive.svg new file mode 100644 index 0000000000..d9b4f0ba65 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_scraper_inactive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_light/status_scraper_no_convergence.svg b/src/qt/res/icons/icons_light/status_scraper_no_convergence.svg new file mode 100644 index 0000000000..0f2a2742e7 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_scraper_no_convergence.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_light/status_scraper_ok.svg b/src/qt/res/icons/icons_light/status_scraper_ok.svg new file mode 100644 index 0000000000..eed7a104e9 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_scraper_ok.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_light/status_scraper_waiting.svg b/src/qt/res/icons/icons_light/status_scraper_waiting.svg new file mode 100644 index 0000000000..ebf5438a43 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_scraper_waiting.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_light/status_staking_no.svg b/src/qt/res/icons/icons_light/status_staking_no.svg new file mode 100644 index 0000000000..1919e4bcf5 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_staking_no.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_staking_problem.svg b/src/qt/res/icons/icons_light/status_staking_problem.svg new file mode 100644 index 0000000000..c95102b923 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_staking_problem.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/status_staking_yes.svg b/src/qt/res/icons/icons_light/status_staking_yes.svg new file mode 100644 index 0000000000..11442fe376 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_staking_yes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_sync_done.svg b/src/qt/res/icons/icons_light/status_sync_done.svg new file mode 100644 index 0000000000..9fbe197480 --- /dev/null +++ b/src/qt/res/icons/icons_light/status_sync_done.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/status_sync_stalled.svg b/src/qt/res/icons/icons_light/status_sync_stalled.svg new file mode 100644 index 0000000000..1683352d6b --- /dev/null +++ b/src/qt/res/icons/icons_light/status_sync_stalled.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/status_sync_syncing.svg b/src/qt/res/icons/icons_light/status_sync_syncing.svg new file mode 100644 index 0000000000..c7cea578cf --- /dev/null +++ b/src/qt/res/icons/icons_light/status_sync_syncing.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/transaction2.png b/src/qt/res/icons/transaction2.png deleted file mode 100644 index 8a804b05ac07ffc7a26f25a774cc58f42fb8273c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vpiiKLn;`P7g&4jT)WtNotvk^ zJ-#FLjS;;eXW1P7aCH24{+JVFlFjhZTIFngLxtAHS(XhygeE;RcX;E{^_JIT)_&%V zikh=p8-55+Vw60RVZ@%Kt&qm_s7U4PbcduP5(dJn9vVt0^K__RyXl=U>%yON-fI|L z9sV$#ZLnRUCMBn^uQ|oR*z#nV*(65BClUhOzO|YUE-Y7NU~n^=Y0W1h7!7nKgQu&X J%Q~loCIG2tN|XQq diff --git a/src/qt/res/icons/transaction2.svg b/src/qt/res/icons/transaction2.svg new file mode 100644 index 0000000000..9fa23557ae --- /dev/null +++ b/src/qt/res/icons/transaction2.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/qt/res/images/gridcoin.svg b/src/qt/res/images/gridcoin.svg index 7913f2c77e..0bcb9a0129 100644 --- a/src/qt/res/images/gridcoin.svg +++ b/src/qt/res/images/gridcoin.svg @@ -1,9 +1,8 @@ - - - + + - - :/icons/beacon_grey - true @@ -201,7 +198,7 @@ - :/icons/synced + :/icons/round_green_check true diff --git a/src/qt/forms/researcherwizardsummarypage.ui b/src/qt/forms/researcherwizardsummarypage.ui index 4a62ae1bb9..b4bb8b718e 100644 --- a/src/qt/forms/researcherwizardsummarypage.ui +++ b/src/qt/forms/researcherwizardsummarypage.ui @@ -152,7 +152,7 @@ - :/icons/synced + :/icons/round_green_check true @@ -455,9 +455,6 @@ - - :/icons/beacon_grey - true diff --git a/src/qt/researcher/projecttablemodel.cpp b/src/qt/researcher/projecttablemodel.cpp index 3bbdf7086d..b1deda12ad 100644 --- a/src/qt/researcher/projecttablemodel.cpp +++ b/src/qt/researcher/projecttablemodel.cpp @@ -176,12 +176,12 @@ QVariant ProjectTableModel::data(const QModelIndex &index, int role) const switch (index.column()) { case Eligible: if (row->m_error.isEmpty()) { - return QIcon(":/icons/synced"); + return QIcon(":/icons/round_green_check"); } break; case Whitelisted: if (row->m_whitelisted) { - return QIcon(":/icons/synced"); + return QIcon(":/icons/round_green_check"); } break; } diff --git a/src/qt/researcher/researcherwizardpoolsummarypage.cpp b/src/qt/researcher/researcherwizardpoolsummarypage.cpp index 6750f7f086..0dadc02d7a 100644 --- a/src/qt/researcher/researcherwizardpoolsummarypage.cpp +++ b/src/qt/researcher/researcherwizardpoolsummarypage.cpp @@ -78,7 +78,7 @@ void ResearcherWizardPoolSummaryPage::refresh() if (m_researcher_model->hasPoolProjects()) { ui->poolStatusLabel->setText(tr("Pool projects detected")); ui->poolStatusIconLabel->setPixmap( - QIcon(":/icons/synced").pixmap(icon_size, icon_size)); + QIcon(":/icons/round_green_check").pixmap(icon_size, icon_size)); } else { ui->poolStatusLabel->setText(tr("No pool projects detected")); ui->poolStatusIconLabel->setPixmap( diff --git a/src/qt/researcher/researcherwizardprojectspage.cpp b/src/qt/researcher/researcherwizardprojectspage.cpp index f4ce0c770e..96bf4dd4de 100644 --- a/src/qt/researcher/researcherwizardprojectspage.cpp +++ b/src/qt/researcher/researcherwizardprojectspage.cpp @@ -86,7 +86,7 @@ void ResearcherWizardProjectsPage::refresh() if (m_researcher_model->hasEligibleProjects()) { ui->selectedCpidIconLabel->setPixmap( - QIcon(":/icons/synced").pixmap(icon_size, icon_size)); + QIcon(":/icons/round_green_check").pixmap(icon_size, icon_size)); } else { ui->selectedCpidIconLabel->setPixmap( QIcon(":/icons/white_and_red_x").pixmap(icon_size, icon_size)); diff --git a/src/qt/researcher/researcherwizardsummarypage.cpp b/src/qt/researcher/researcherwizardsummarypage.cpp index 5ec691aa2e..a1d43952ab 100644 --- a/src/qt/researcher/researcherwizardsummarypage.cpp +++ b/src/qt/researcher/researcherwizardsummarypage.cpp @@ -114,19 +114,19 @@ void ResearcherWizardSummaryPage::refreshOverallStatus() if (m_researcher_model->outOfSync()) { status = tr("Waiting for sync..."); - icon = QIcon(":/icons/notsynced"); + icon = QIcon(":/icons/status_sync_syncing_light"); } else if (m_researcher_model->hasPendingBeacon()) { status = tr("Beacon awaiting confirmation."); - icon = QIcon(":/icons/notsynced"); + icon = QIcon(":/icons/transaction_3"); } else if (m_researcher_model->hasRenewableBeacon()) { status = tr("Beacon renewal available."); icon = QIcon(":/icons/warning"); } else if (!m_researcher_model->hasMagnitude()) { status = tr("Waiting for magnitude."); - icon = QIcon(":/icons/notsynced"); + icon = QIcon(":/icons/scraper_waiting_light"); } else { status = tr("Everything looks good."); - icon = QIcon(":/icons/synced"); + icon = QIcon(":/icons/round_green_check"); } ui->overallStatusLabel->setText(status); @@ -147,7 +147,7 @@ void ResearcherWizardSummaryPage::refreshProjects() if (m_researcher_model->hasEligibleProjects()) { ui->selectedCpidIconLabel->setPixmap( - QIcon(":/icons/synced").pixmap(icon_size, icon_size)); + QIcon(":/icons/round_green_check").pixmap(icon_size, icon_size)); } else { ui->selectedCpidIconLabel->setPixmap( QIcon(":/icons/white_and_red_x").pixmap(icon_size, icon_size)); From 18c8de90d469463ea1c5be55cf1d3c5da2736dd1 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:41 -0500 Subject: [PATCH 034/280] Implement theme support for GUI researcher model This enables the GUI researcher model to respond to changes to the application stylesheets to update icons and colors. --- src/qt/bitcoin.cpp | 2 +- src/qt/bitcoingui.cpp | 10 +++- src/qt/researcher/researchermodel.cpp | 48 ++++++++++++------- src/qt/researcher/researchermodel.h | 4 +- .../researcher/researcherwizardbeaconpage.cpp | 2 +- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bfca9de0a2..acd317009b 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -560,9 +560,9 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt WalletModel walletModel(pwalletMain, &optionsModel); ResearcherModel researcherModel; + window.setResearcherModel(&researcherModel); window.setClientModel(&clientModel); window.setWalletModel(&walletModel); - window.setResearcherModel(&researcherModel); // If -min option passed, start window minimized. if(GetBoolArg("-min")) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4f01acf901..6b176a5230 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -261,11 +261,19 @@ void BitcoinGUI::setOptionsStyleSheet(QString qssFileName) qApp->setStyleSheet(sMainWindowHTML); } + sSheet=qssFileName; setIcons(); + // reset encryption status to apply icon color changes - if(walletModel) + if (walletModel) { setEncryptionStatus(walletModel->getEncryptionStatus()); + } + + // Reapply the appropriate beacon icon color scheme: + if (researcherModel) { + researcherModel->setTheme(sSheet); + } } diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 80603b1163..08e119cdac 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -87,6 +87,7 @@ ResearcherModel::ResearcherModel() , m_configured_for_investor_mode(false) , m_wizard_open(false) , m_out_of_sync(true) + , m_theme_suffix("_light") { qRegisterMetaType("GRC::ResearcherPtr"); @@ -141,27 +142,31 @@ QString ResearcherModel::mapBeaconStatus(const BeaconStatus status) assert(false); // Suppress warning } -QIcon ResearcherModel::mapBeaconStatusIcon(const BeaconStatus status) +QIcon ResearcherModel::mapBeaconStatusIcon(const BeaconStatus status) const { - constexpr char success[] = ":/icons/beacon_green"; - constexpr char warning[] = ":/icons/beacon_yellow"; - constexpr char danger[] = ":/icons/beacon_red"; - constexpr char inactive[] = ":/icons/beacon_grey"; + constexpr char success[] = ":/icons/status_beacon_green"; + constexpr char warning[] = ":/icons/status_beacon_yellow"; + constexpr char danger[] = ":/icons/status_beacon_red"; + constexpr char inactive[] = ":/icons/status_beacon_gray"; + + const auto make_icon = [this](const char* const icon) { + return QIcon(icon + m_theme_suffix); + }; switch (status) { - case BeaconStatus::ACTIVE: return QIcon(success); - case BeaconStatus::ERROR_INSUFFICIENT_FUNDS: return QIcon(danger); - case BeaconStatus::ERROR_MISSING_KEY: return QIcon(danger); - case BeaconStatus::ERROR_NOT_NEEDED: return QIcon(success); - case BeaconStatus::ERROR_TX_FAILED: return QIcon(danger); - case BeaconStatus::ERROR_WALLET_LOCKED: return QIcon(danger); - case BeaconStatus::NO_BEACON: return QIcon(inactive); - case BeaconStatus::NO_CPID: return QIcon(inactive); - case BeaconStatus::NO_MAGNITUDE: return QIcon(warning); - case BeaconStatus::PENDING: return QIcon(warning); - case BeaconStatus::RENEWAL_NEEDED: return QIcon(danger); - case BeaconStatus::RENEWAL_POSSIBLE: return QIcon(warning); - case BeaconStatus::UNKNOWN: return QIcon(inactive); + case BeaconStatus::ACTIVE: return make_icon(success); + case BeaconStatus::ERROR_INSUFFICIENT_FUNDS: return make_icon(danger); + case BeaconStatus::ERROR_MISSING_KEY: return make_icon(danger); + case BeaconStatus::ERROR_NOT_NEEDED: return make_icon(success); + case BeaconStatus::ERROR_TX_FAILED: return make_icon(danger); + case BeaconStatus::ERROR_WALLET_LOCKED: return make_icon(danger); + case BeaconStatus::NO_BEACON: return make_icon(inactive); + case BeaconStatus::NO_CPID: return make_icon(inactive); + case BeaconStatus::NO_MAGNITUDE: return make_icon(warning); + case BeaconStatus::PENDING: return make_icon(warning); + case BeaconStatus::RENEWAL_NEEDED: return make_icon(danger); + case BeaconStatus::RENEWAL_POSSIBLE: return make_icon(warning); + case BeaconStatus::UNKNOWN: return make_icon(inactive); } assert(false); // Suppress warning @@ -190,6 +195,13 @@ void ResearcherModel::showWizard(WalletModel* wallet_model) wizard->show(); } +void ResearcherModel::setTheme(const QString& theme_name) +{ + m_theme_suffix = "_" + theme_name; + + emit beaconChanged(); +} + bool ResearcherModel::configuredForInvestorMode() const { return m_configured_for_investor_mode; diff --git a/src/qt/researcher/researchermodel.h b/src/qt/researcher/researchermodel.h index 40f6b9c469..2267d781f1 100644 --- a/src/qt/researcher/researchermodel.h +++ b/src/qt/researcher/researchermodel.h @@ -77,9 +77,10 @@ class ResearcherModel : public QObject ~ResearcherModel(); static QString mapBeaconStatus(const BeaconStatus status); - static QIcon mapBeaconStatusIcon(const BeaconStatus status); + QIcon mapBeaconStatusIcon(const BeaconStatus status) const; void showWizard(WalletModel* wallet_model); + void setTheme(const QString& theme_name); bool configuredForInvestorMode() const; bool outOfSync() const; @@ -119,6 +120,7 @@ class ResearcherModel : public QObject bool m_configured_for_investor_mode; bool m_wizard_open; bool m_out_of_sync; + QString m_theme_suffix; void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); diff --git a/src/qt/researcher/researcherwizardbeaconpage.cpp b/src/qt/researcher/researcherwizardbeaconpage.cpp index 7425b98c3e..1b786de2ed 100644 --- a/src/qt/researcher/researcherwizardbeaconpage.cpp +++ b/src/qt/researcher/researcherwizardbeaconpage.cpp @@ -113,7 +113,7 @@ void ResearcherWizardBeaconPage::advertiseBeacon() } updateBeaconStatus(ResearcherModel::mapBeaconStatus(status)); - updateBeaconIcon(ResearcherModel::mapBeaconStatusIcon(status)); + updateBeaconIcon(m_researcher_model->mapBeaconStatusIcon(status)); } void ResearcherWizardBeaconPage::updateBeaconStatus(const QString& status) From 5336992392170906a275d06599471293c7153895 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:46 -0500 Subject: [PATCH 035/280] Update GUI sidebar styles This changes the styles of the GUI side toolbar to match the proposed redesign. It removes the top toolbar which contained the Gridcoin and BOINC logos. --- src/qt/bitcoingui.cpp | 172 +++++++++++++------ src/qt/bitcoingui.h | 30 ++++ src/qt/res/stylesheets/dark_stylesheet.qss | 90 +++------- src/qt/res/stylesheets/light_stylesheet.qss | 93 +++------- src/qt/res/stylesheets/native_stylesheet.qss | 10 -- 5 files changed, 198 insertions(+), 197 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6b176a5230..bb74dcd443 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -296,12 +296,12 @@ void BitcoinGUI::createActions() receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); - historyAction = new QAction(tr("&Transactions"), tabGroup); + historyAction = new QAction(tr("&History"), tabGroup); historyAction->setToolTip(tr("Browse transaction history")); historyAction->setCheckable(true); historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); - addressBookAction = new QAction(tr("&Address Book"), tabGroup); + addressBookAction = new QAction(tr("&Favorites"), tabGroup); addressBookAction->setToolTip(tr("Edit the list of stored addresses and labels")); addressBookAction->setCheckable(true); addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); @@ -416,17 +416,59 @@ void BitcoinGUI::createActions() void BitcoinGUI::setIcons() { - overviewAction->setIcon(QPixmap(":/icons/overview_"+sSheet)); - sendCoinsAction->setIcon(QPixmap(":/icons/send_"+sSheet)); - receiveCoinsAction->setIcon(QPixmap(":/icons/receiving_addresses_"+sSheet)); - historyAction->setIcon(QPixmap(":/icons/history_"+sSheet)); - addressBookAction->setIcon(QPixmap(":/icons/address-book_"+sSheet)); - votingAction->setIcon(QPixmap(":/icons/voting_"+sSheet)); - unlockWalletAction->setIcon(QPixmap(":/icons/lock_open_"+sSheet)); - lockWalletAction->setIcon(QPixmap(":/icons/lock_closed_"+sSheet)); + const QToolBar* toolbar = findChild(); + const int toolbar_icon_size = 16 * logicalDpiX() / 96; + + ToolbarButtonIconFilter::apply( + this, + overviewAction, + toolbar->widgetForAction(overviewAction), + toolbar_icon_size, + ":/icons/overview_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + sendCoinsAction, + toolbar->widgetForAction(sendCoinsAction), + toolbar_icon_size, + ":/icons/send_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + receiveCoinsAction, + toolbar->widgetForAction(receiveCoinsAction), + toolbar_icon_size, + ":/icons/receive_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + historyAction, + toolbar->widgetForAction(historyAction), + toolbar_icon_size, + ":/icons/history_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + addressBookAction, + toolbar->widgetForAction(addressBookAction), + toolbar_icon_size, + ":/icons/address-book_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + votingAction, + toolbar->widgetForAction(votingAction), + toolbar_icon_size, + ":/icons/voting_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + unlockWalletAction, + toolbar->widgetForAction(unlockWalletAction), + toolbar_icon_size, + ":/icons/lock_open_" + sSheet); + ToolbarButtonIconFilter::apply( + this, + lockWalletAction, + toolbar->widgetForAction(lockWalletAction), + toolbar_icon_size, + ":/icons/lock_closed_" + sSheet); encryptWalletAction->setIcon(QPixmap(":/icons/lock_closed_"+sSheet)); - bxAction->setIcon(QPixmap(":/icons/block")); exchangeAction->setIcon(QPixmap(":/icons/ex")); websiteAction->setIcon(QPixmap(":/icons/www")); @@ -508,6 +550,10 @@ void BitcoinGUI::createMenuBar() void BitcoinGUI::createToolBars() { + ClickLabel *logoLabel = new ClickLabel(); + logoLabel->setObjectName("toolbarLogoLabel"); + connect(logoLabel, SIGNAL(clicked()), this, SLOT(websiteClicked())); + // "Tabs" toolbar (vertical, aligned on left side of overview screen). QToolBar *toolbar = addToolBar("Tabs toolbar"); toolbar->setObjectName("toolbar"); @@ -516,19 +562,17 @@ void BitcoinGUI::createToolBars() toolbar->setMovable(false); toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); toolbar->setContextMenuPolicy(Qt::PreventContextMenu); - toolbar->setIconSize(QSize(50 * logicalDpiX() / 96, 25 * logicalDpiX() / 96)); + // Setting a taller height than the rendered icon provides additional + // padding between the icon and the button text: + toolbar->setIconSize(QSize(16 * logicalDpiX() / 96, 24 * logicalDpiX() / 96)); + toolbar->addWidget(logoLabel); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); toolbar->addAction(addressBookAction); toolbar->addAction(votingAction); - - QWidget* spacer = new QWidget(); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - toolbar->addWidget(spacer); - spacer->setObjectName("spacer"); - // Unlock Wallet + toolbar->addSeparator(); toolbar->addAction(unlockWalletAction); toolbar->addAction(lockWalletAction); @@ -586,40 +630,6 @@ void BitcoinGUI::createToolBars() toolbar2->addWidget(frameBlocks); addToolBarBreak(Qt::TopToolBarArea); - - - // Top tool bar (clickable Gridcoin and BOINC logos) - QToolBar *toolbar3 = addToolBar("Logo bar"); - addToolBar(Qt::TopToolBarArea, toolbar3); - toolbar3->setOrientation(Qt::Horizontal); - toolbar3->setMovable(false); - toolbar3->setObjectName("toolbar3"); - ClickLabel *grcLogoLabel = new ClickLabel(); - grcLogoLabel->setObjectName("gridcoinLogoHorizontal"); - connect(grcLogoLabel, SIGNAL(clicked()), this, SLOT(websiteClicked())); - toolbar3->addWidget(grcLogoLabel); - QWidget* logoSpacer = new QWidget(); - logoSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - toolbar3->addWidget(logoSpacer); - logoSpacer->setObjectName("logoSpacer"); - ClickLabel *boincLogoLabel = new ClickLabel(); - boincLogoLabel->setObjectName("boincLogo"); - connect(boincLogoLabel, SIGNAL(clicked()), this, SLOT(boincClicked())); - toolbar3->addWidget(boincLogoLabel); - - // Use a red color for the toolbars background if on testnet. - if (GetBoolArg("-testnet")) - { - toolbar2->setStyleSheet("background-color:darkRed"); - toolbar3->setStyleSheet("background-color:darkRed"); - } - else - { - toolbar2->setStyleSheet("background-color:rgb(65,0,127)"); - toolbar3->setStyleSheet("background-color:rgb(65,0,127)"); - } - - } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -1212,7 +1222,6 @@ void BitcoinGUI::gotoSendCoinsPage() void BitcoinGUI::gotoVotingPage() { votingAction->setChecked(true); - //votingPage->loadPolls(false); centralWidget->setCurrentWidget(votingPage); exportAction->setEnabled(false); @@ -1668,3 +1677,60 @@ void BitcoinGUI::updateBeaconIcon() .arg(researcherModel->formatTimeToBeaconExpiration()) .arg(researcherModel->formatBeaconStatus())); } + +// ----------------------------------------------------------------------------- +// Class: ToolbarButtonIconFilter +// ----------------------------------------------------------------------------- + +ToolbarButtonIconFilter::ToolbarButtonIconFilter( + QObject* parent, + QIcon resting_icon, + QIcon hover_icon) + : QObject(parent) + , m_resting_icon(std::move(resting_icon)) + , m_hover_icon(std::move(hover_icon)) +{ +} + +void ToolbarButtonIconFilter::apply( + QObject* parent, + QAction* tool_action, + QWidget* tool_button, + const int icon_size, + const QString& base_icon_path) +{ + const QPixmap inactive_pixmap = QIcon(base_icon_path).pixmap(icon_size, icon_size); + const QPixmap active_pixmap = QIcon(base_icon_path + "_active").pixmap(icon_size, icon_size); + + QIcon hover_icon(active_pixmap); + QIcon resting_icon; + resting_icon.addPixmap(inactive_pixmap, QIcon::Normal, QIcon::Off); + resting_icon.addPixmap(active_pixmap, QIcon::Normal, QIcon::On); + + tool_action->setIcon(resting_icon); + tool_button->installEventFilter(new ToolbarButtonIconFilter( + parent, + std::move(resting_icon), + std::move(hover_icon))); +} + +bool ToolbarButtonIconFilter::eventFilter(QObject* target, QEvent* event) +{ + QToolButton* button = qobject_cast(target); + + if (!button || button->isChecked()) { + return false; + } + + if (event->type() == QEvent::Enter) { + button->setIcon(m_hover_icon); + return true; + } + + if (event->type() == QEvent::Leave) { + button->setIcon(m_resting_icon); + return true; + } + + return false; +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 771ba376ea..bbdc68a8b9 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -263,4 +263,34 @@ private slots: void updateGlobalStatus(); }; +//! +//! \brief Sets up and toggles hover states for the main toolbar icons. +//! +//! Qt stylesheets do not provide a way to manage QToolButton resting, active +//! and hover states for the button icons in a way that scales for a high-DPI +//! desktop. This class provides an event filter implementation that switches +//! the tool button icons in response to hover events. +//! +class ToolbarButtonIconFilter : public QObject +{ + Q_OBJECT + +public: + explicit ToolbarButtonIconFilter(QObject* parent, QIcon resting_icon, QIcon hover_icon); + virtual ~ToolbarButtonIconFilter() { } + + static void apply( + QObject* parent, + QAction* tool_action, + QWidget* tool_button, + const int icon_size, + const QString& base_icon_path); + + bool eventFilter(QObject* watched, QEvent* event) override; + +private: + QIcon m_resting_icon; + QIcon m_hover_icon; +}; // ToolbarButtonIconFilter + #endif diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index afd33e666f..aeca497fd5 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -54,22 +54,6 @@ QFrame[frameShape="5"] { border-left: 0.065em solid rgb(58, 70, 94); } -QToolBar#toolbar QToolButton { - color: white; - background-color: transparent; - border: none; - border-radius: 0; -} - -QToolBar#toolbar QToolButton:hover { - color: white; - background-color: rgb(26, 145, 235); -} - -QToolBar#toolbar QToolButton:checked { - background-color: rgb(64,68,82); -} - QMenu { border: 0.065em solid rgb(58, 70, 94); } @@ -435,58 +419,38 @@ QTabBar::tab:!selected:hover { /* Main Window*/ -#toolbar { - border:none; - padding-top:1.25em; - min-width:8.5em; - height:100%; - color: white; -} - -#toolbar2 { - border:none; - min-width:1.625em; - max-width:3em; - color: white; -} - -#toolbar3 { - border:none; - min-height:3.25em; - max-height:3.25em; +QToolBar#toolbar { + border-right: 0.065em solid rgb(18, 26, 34); + height: 100%; } QToolBar#toolbar QToolButton { - padding-left:1.25em; - padding-right:1.25em; - padding-top:0.5em; - padding-bottom:0.5em; - margin:0.25em; - width:100%; -} - -QToolBar#toolbar2 .QFrame { - padding: 0; -} - -QToolBar#toolbar2 QLabel{ - border:none; - padding-top:1em; - padding-bottom:1em; - qproperty-alignment: AlignCenter; + width: 100%; + min-width: 6em; + background-color: transparent; + border: none; + font-weight: 500; + padding: 0.3em 0 0.25em 0; } -QToolBar#toolbar2 QToolTip { - color:white +QToolBar#toolbar QToolButton:hover, +QToolBar#toolbar QToolButton:checked { + color: rgb(26, 145, 235); } -QToolBar#toolbar3 QLabel{ - margin:0.5em; +QToolBar#toolbar::separator { + background: none; + border-top: 0.065em solid rgb(204, 208, 209); + height: 0.065em; + margin: 0.25em 1.5em; } -#spacer { - background-color:transparent; - border:none; +#toolbarLogoLabel { + image: url(:/images/gridcoin); + border-bottom: 0.065em solid rgb(18, 26, 34); + min-width: 2.75em; + min-height: 2.75em; + padding: 1em 1.5em; } #boincLogo{ @@ -497,14 +461,6 @@ QToolBar#toolbar3 QLabel{ max-height:2.25em; } -#gridcoinLogoHorizontal{ - image:url(:/images/logo_hz); - min-width:9.225em; - max-width:9.225em; - min-height:2.25em; - max-height:2.25em; -} - #aboutLogoLabel{ image:url(:/images/about); min-width:2.25em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 133b2da136..0d947cd15b 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -50,22 +50,6 @@ QFrame[frameShape="5"] { border-left: 0.065em solid rgb(234, 237, 237); } -QToolBar#toolbar QToolButton { - color: black; - background-color: transparent; - border: none; - border-radius: 0; -} - -QToolBar#toolbar QToolButton:hover { - color:white; - background-color: rgb(65,0,127); -} - -QToolBar#toolbar QToolButton:checked { - background-color: rgb(200,200,200); -} - QMenu { background-color: white; border: 0.065em solid rgb(224, 227, 227); @@ -445,58 +429,41 @@ QTabBar::tab:!selected:hover { /* Main Window*/ -#toolbar { - border:none; - padding-top:1.25em; - min-width:8.5em; - height:100%; - color: black; -} - -#toolbar2 { - border:none; - min-width:1.625em; - max-width:3em; - color: white; -} - -#toolbar3 { - border:none; - min-height:3.25em; - max-height:3.25em; +QToolBar#toolbar { + background-color: rgb(54, 1, 102); + border: none; + height: 100%; } QToolBar#toolbar QToolButton { - padding-left:1.25em; - padding-right:1.25em; - padding-top:0.5em; - padding-bottom:0.5em; - margin:0.25em; - width:100%; -} - -QToolBar#toolbar2 .QFrame { - padding: 0; -} - -QToolBar#toolbar2 QLabel{ - border:none; - padding-top:1em; - padding-bottom:1em; - qproperty-alignment: AlignCenter; + width: 100%; + min-width: 6em; + background-color: transparent; + border: none; + color: rgb(154, 128, 178); + font-weight: 500; + padding: 0.3em 0 0.25em 0; } -QToolBar#toolbar2 QToolTip { - color:white +QToolBar#toolbar QToolButton:hover, +QToolBar#toolbar QToolButton:checked { + color: white; } -QToolBar#toolbar3 QLabel{ - margin:0.5em; +QToolBar#toolbar::separator { + background: none; + border-top: 0.065em solid rgb(154, 128, 178); + height: 0.065em; + margin: 0.25em 1.5em; } -#spacer { - background-color:transparent; - border:none; +#toolbarLogoLabel { + image: url(:/images/gridcoin); + background-color: rgb(48, 11, 82); + border-bottom: 0.065em solid rgb(74, 26, 117); + min-width: 2.75em; + min-height: 2.75em; + padding: 1em 1.5em; } #boincLogo{ @@ -507,14 +474,6 @@ QToolBar#toolbar3 QLabel{ max-height:2.25em; } -#gridcoinLogoHorizontal{ - image:url(:/images/logo_hz); - min-width:9.225em; - max-width:9.225em; - min-height:2.25em; - max-height:2.25em; -} - #aboutLogoLabel{ image:url(:/images/about_light); min-width:2.25em; diff --git a/src/qt/res/stylesheets/native_stylesheet.qss b/src/qt/res/stylesheets/native_stylesheet.qss index d38d60df3a..9cb3a70753 100644 --- a/src/qt/res/stylesheets/native_stylesheet.qss +++ b/src/qt/res/stylesheets/native_stylesheet.qss @@ -47,12 +47,6 @@ QTextEdit { max-width:3em; } -#toolbar3 { - border:none; - min-height:3.25em; - max-height:3.25em; -} - QToolBar#toolbar QToolButton { padding-left:1.25em; padding-right:1.25em; @@ -69,10 +63,6 @@ QToolBar#toolbar2 QLabel{ qproperty-alignment: AlignCenter; } -QToolBar#toolbar3 QLabel{ - margin:0.5em; -} - #spacer { background-color:transparent; border:none; From 9597853645a856309737adee4da4e6a344a4659e Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:51 -0500 Subject: [PATCH 036/280] Update GUI status bar styles This updates the styles of the GUI status bar to match the proposed redesign. It moves the status bar from the left to the bottom. --- src/qt/bitcoingui.cpp | 87 ++++++++++--------- .../icons_dark/status_connections_average.svg | 9 +- .../icons_dark/status_connections_none.svg | 10 ++- .../icons_dark/status_connections_poor.svg | 9 +- .../status_connections_average.svg | 9 +- .../icons_light/status_connections_none.svg | 10 ++- .../icons_light/status_connections_poor.svg | 9 +- src/qt/res/stylesheets/dark_stylesheet.qss | 32 +++++++ src/qt/res/stylesheets/light_stylesheet.qss | 28 ++++++ 9 files changed, 157 insertions(+), 46 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bb74dcd443..d515ccf3e7 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -198,7 +198,10 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): setCentralWidget(centralWidget); // Create status bar - // statusBar(); + statusBar(); + + // Disable size grip because it looks ugly and nobody needs it + statusBar()->setSizeGripEnabled(false); // Clicking on a transaction on the overview page simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); @@ -578,25 +581,25 @@ void BitcoinGUI::createToolBars() addToolBarBreak(Qt::LeftToolBarArea); - // Status bar notification icons (Status toolbar) - QToolBar *toolbar2 = addToolBar("Status toolbar"); - addToolBar(Qt::LeftToolBarArea, toolbar2); - toolbar2->setOrientation(Qt::Vertical); - //toolbar2->setGeometry(0, 0, STATUSBAR_ICONSIZE, 0); - toolbar2->setMinimumWidth(STATUSBAR_ICONSIZE); - toolbar2->setContentsMargins(0, 0, 0, 0); - toolbar2->setMovable(false); - toolbar2->setObjectName("toolbar2"); - QFrame *frameBlocks = new QFrame(); + // Show a red label in the status bar for testnet: + if (GetBoolArg("-testnet")) { + QLabel *testnetLabel = new QLabel(); + testnetLabel->setObjectName("testnetStatusLabel"); + testnetLabel->setText("TESTNET"); + + statusBar()->addWidget(testnetLabel); + } + frameBlocks->setContentsMargins(0,0,0,0); - frameBlocks->setMinimumWidth(STATUSBAR_ICONSIZE); + frameBlocks->setMinimumHeight(STATUSBAR_ICONSIZE); - QVBoxLayout *frameBlocksLayout = new QVBoxLayout(frameBlocks); - frameBlocksLayout->setContentsMargins(1,0,1,0); - frameBlocksLayout->setSpacing(-1); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + frameBlocksLayout->setContentsMargins(5, 0, 5, 0); + frameBlocksLayout->setSpacing(3); labelEncryptionIcon = new QLabel(); labelStakingIcon = new QLabel(); labelConnectionsIcon = new ClickLabel(); @@ -606,12 +609,12 @@ void BitcoinGUI::createToolBars() labelBeaconIcon = new ClickLabel(); connect(labelBeaconIcon, SIGNAL(clicked()), this, SLOT(researcherClicked())); - frameBlocksLayout->addWidget(labelEncryptionIcon); + frameBlocksLayout->addWidget(labelBeaconIcon); + frameBlocksLayout->addWidget(labelBlocksIcon); frameBlocksLayout->addWidget(labelStakingIcon); frameBlocksLayout->addWidget(labelConnectionsIcon); - frameBlocksLayout->addWidget(labelBlocksIcon); frameBlocksLayout->addWidget(labelScraperIcon); - frameBlocksLayout->addWidget(labelBeaconIcon); + frameBlocksLayout->addWidget(labelEncryptionIcon); //12-21-2015 Prevent Lock from falling off the page frameBlocksLayout->addStretch(); @@ -623,11 +626,11 @@ void BitcoinGUI::createToolBars() timerStakingIcon->start(MODEL_UPDATE_DELAY); // Instead of calling updateStakingIcon here, simply set the icon to staking off. // This is to prevent problems since this GUI code can initialize before the core. - labelStakingIcon->setPixmap(QIcon(":/icons/staking_off").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_no_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelStakingIcon->setToolTip(tr("Not staking: Miner is not initialized.")); } - toolbar2->addWidget(frameBlocks); + statusBar()->addPermanentWidget(frameBlocks); addToolBarBreak(Qt::TopToolBarArea); } @@ -846,16 +849,19 @@ void BitcoinGUI::setNumConnections(int n) QString icon; switch (n) { - case 0: icon = ":/icons/connect_0"; break; - case 1: case 2: case 3: icon = ":/icons/connect_1"; break; - case 4: case 5: case 6: icon = ":/icons/connect_2"; break; - case 7: case 8: case 9: icon = ":/icons/connect_3"; break; - default: icon = ":/icons/connect_4"; break; + case 0: icon = ":/icons/status_connection_0"; break; + case 1: case 2: icon = ":/icons/status_connection_1"; break; + case 3: case 4: case 5: icon = ":/icons/status_connection_2"; break; + case 6: case 7: case 8: case 9: icon = ":/icons/status_connection_3"; break; + default: icon = ":/icons/status_connection_4"; break; } + + icon.append("_").append(sSheet); labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); if (n == 0) { + setNumBlocks(0, 0); // Counts don't matter--just make it red labelConnectionsIcon->setToolTip(tr("No active connections to the Gridcoin network. " "If this persists more than a few minutes, please check your configuration " "and your network connectivity.")); @@ -871,6 +877,8 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) // return if we have no connection to the network if (!clientModel || clientModel->getNumConnections() == 0) { + labelBlocksIcon->setPixmap(QIcon(":/icons/status_sync_stalled_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setToolTip(tr("Sync: no connections.")); return; } @@ -907,13 +915,13 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) if(secs < 90*60 && count >= nTotalBlocks) { tooltip = tr("Up to date") + QString(".
") + tooltip; - labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setPixmap(QIcon(":/icons/status_sync_done_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); overviewPage->showOutOfSyncWarning(false); } else { - labelBlocksIcon->setPixmap(QIcon(":/icons/notsynced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setPixmap(QIcon(":/icons/status_sync_syncing_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); tooltip = tr("Catching up...") + QString("
") + tooltip; overviewPage->showOutOfSyncWarning(true); @@ -1292,7 +1300,8 @@ void BitcoinGUI::setEncryptionStatus(int status) switch(status) { case WalletModel::Unencrypted: - labelEncryptionIcon->hide(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_none_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is not encrypted!")); encryptWalletAction->setChecked(false); changePassphraseAction->setEnabled(false); unlockWalletAction->setVisible(false); @@ -1300,8 +1309,7 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: - labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open_"+sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_unlocked_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently %1 ").arg(fWalletUnlockStakingOnly ? tr("unlocked for staking only") : tr("fully unlocked"))); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -1310,8 +1318,7 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::Locked: - labelEncryptionIcon->show(); - labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed_"+sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_locked_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -1496,7 +1503,7 @@ void BitcoinGUI::updateStakingIcon() if (globalStatus.staking) { - labelStakingIcon->setPixmap(QIcon(":/icons/staking_on").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_yes_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelStakingIcon->setToolTip(tr("Staking.
Your weight is %1
Network weight is %2
Estimated staking frequency is %3.") .arg(QString::number(globalStatus.coinWeight, 'f', 0)) .arg(QString::number(globalStatus.netWeight, 'f', 0)) @@ -1513,7 +1520,7 @@ void BitcoinGUI::updateStakingIcon() } else if (!globalStatus.staking && !globalStatus.able_to_stake) { - labelStakingIcon->setPixmap(QIcon(":/icons/staking_unable").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_problem_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); //Part of this string won't be translated :( labelStakingIcon->setToolTip(tr("Unable to stake: %1") .arg(QString(globalStatus.ReasonNotStaking.c_str()))); @@ -1529,7 +1536,7 @@ void BitcoinGUI::updateStakingIcon() } else { - labelStakingIcon->setPixmap(QIcon(":/icons/staking_off").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_no_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); //Part of this string won't be translated :( labelStakingIcon->setToolTip(tr("Not staking currently: %1, Estimated staking frequency is %2.") .arg(QString(globalStatus.ReasonNotStaking.c_str())) @@ -1604,23 +1611,23 @@ void BitcoinGUI::updateScraperIcon(int scraperEventtype, int status) if (scraperEventtype == (int)scrapereventtypes::OutOfSync && status == CT_UPDATING) { - labelScraperIcon->setPixmap(QIcon(":/icons/notsynced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_waiting_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelScraperIcon->setToolTip(tr("Scraper: waiting on wallet to sync.")); } else if (scraperEventtype == (int)scrapereventtypes::Sleep && status == CT_NEW) { - labelScraperIcon->setPixmap(QIcon(":/icons/gray_scraper").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_inactive_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelScraperIcon->setToolTip(tr("Scraper: superblock not needed - inactive.")); } else if (scraperEventtype == (int)scrapereventtypes::Stats && (status == CT_NEW || status == CT_UPDATED || status == CT_UPDATING)) { - labelScraperIcon->setPixmap(QIcon(":/icons/notsynced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_waiting_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelScraperIcon->setToolTip(tr("Scraper: downloading and processing stats.")); } else if ((scraperEventtype == (int)scrapereventtypes::Convergence || scraperEventtype == (int)scrapereventtypes::SBContract) && (status == CT_NEW || status == CT_UPDATED) && nConvergenceTime) { - labelScraperIcon->setPixmap(QIcon(":/icons/green_scraper").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_ok_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); if (bDisplayScrapers) { @@ -1646,7 +1653,7 @@ void BitcoinGUI::updateScraperIcon(int scraperEventtype, int status) else if ((scraperEventtype == (int)scrapereventtypes::Convergence || scraperEventtype == (int)scrapereventtypes::SBContract) && status == CT_DELETED) { - labelScraperIcon->setPixmap(QIcon(":/icons/white_and_red_x").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_no_convergence_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelScraperIcon->setToolTip(tr("Scraper: No convergence able to be achieved. Will retry in a few minutes.")); } diff --git a/src/qt/res/icons/icons_dark/status_connections_average.svg b/src/qt/res/icons/icons_dark/status_connections_average.svg index 10bed710fe..82de7793ed 100644 --- a/src/qt/res/icons/icons_dark/status_connections_average.svg +++ b/src/qt/res/icons/icons_dark/status_connections_average.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + + + + diff --git a/src/qt/res/icons/icons_dark/status_connections_none.svg b/src/qt/res/icons/icons_dark/status_connections_none.svg index a2b31b53c2..21d27d69c6 100644 --- a/src/qt/res/icons/icons_dark/status_connections_none.svg +++ b/src/qt/res/icons/icons_dark/status_connections_none.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/src/qt/res/icons/icons_dark/status_connections_poor.svg b/src/qt/res/icons/icons_dark/status_connections_poor.svg index b0a9d8418c..dc09ab73a8 100644 --- a/src/qt/res/icons/icons_dark/status_connections_poor.svg +++ b/src/qt/res/icons/icons_dark/status_connections_poor.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_connections_average.svg b/src/qt/res/icons/icons_light/status_connections_average.svg index 83fdf77956..552438ceca 100644 --- a/src/qt/res/icons/icons_light/status_connections_average.svg +++ b/src/qt/res/icons/icons_light/status_connections_average.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_connections_none.svg b/src/qt/res/icons/icons_light/status_connections_none.svg index 09774569c4..a5fecfbcb3 100644 --- a/src/qt/res/icons/icons_light/status_connections_none.svg +++ b/src/qt/res/icons/icons_light/status_connections_none.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/src/qt/res/icons/icons_light/status_connections_poor.svg b/src/qt/res/icons/icons_light/status_connections_poor.svg index 212f44da62..dc09ab73a8 100644 --- a/src/qt/res/icons/icons_light/status_connections_poor.svg +++ b/src/qt/res/icons/icons_light/status_connections_poor.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + + + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index aeca497fd5..41e7b746eb 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -453,6 +453,38 @@ QToolBar#toolbar::separator { padding: 1em 1.5em; } +QStatusBar { + background: rgb(23, 23, 23); + border-top: 0.065em solid rgb(19, 19, 19); +} + +QStatusBar::item { + border: none; +} + +QStatusBar .QFrame { + background: transparent; + padding: 0; + margin: 0.25em 0; +} + +QStatusBar .QFrame QLabel { + border: none; + margin: 0 0.25em; +} + +QStatusBar QToolTip { + color: white +} + +#testnetStatusLabel { + background-color: rgb(237, 81, 68); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: white; + font-weight: bold; +} + #boincLogo{ image:url(:/images/boinc_w); min-width:5.38em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 0d947cd15b..8f6d06eba0 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -466,6 +466,34 @@ QToolBar#toolbar::separator { padding: 1em 1.5em; } +QStatusBar { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(243, 243, 243), stop: 1 rgb(212, 212, 212)); + border-top: 0.065em solid rgb(230, 230, 230); +} + +QStatusBar::item { + border: none; +} + +QStatusBar .QFrame { + background: transparent; + padding: 0; + margin: 0.25em 0; +} + +QStatusBar .QFrame QLabel { + border: none; + margin: 0 0.25em; +} + +#testnetStatusLabel { + background-color: rgb(237, 81, 68); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: white; + font-weight: bold; +} + #boincLogo{ image:url(:/images/boinc_w); min-width:5.38em; From ca64a7b6f85d3e8e41260f3fad5d4f0ec454021b Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:17:55 -0500 Subject: [PATCH 037/280] Remove the native GUI theme The proposed GUI redesign does not provide icons sufficient for a native theme that can support both light and dark mode desktops. We can rebuild a native theme in the future. --- src/Makefile.qt.include | 17 +- src/qt/bitcoin.qrc | 18 +- src/qt/forms/signverifymessagedialog.ui | 8 +- src/qt/optionsdialog.cpp | 1 - src/qt/optionsmodel.cpp | 19 +- src/qt/res/icons/beacon_green.svg | 106 ---------- src/qt/res/icons/beacon_grey.svg | 96 --------- src/qt/res/icons/beacon_red.svg | 106 ---------- src/qt/res/icons/beacon_yellow.svg | 25 --- src/qt/res/icons/gray_scraper.svg | 64 ------ src/qt/res/icons/green_check.svg | 55 ----- src/qt/res/icons/green_scraper.svg | 39 ---- .../res/icons/icons_native/address-book.svg | 1 - src/qt/res/icons/icons_native/lock_closed.svg | 1 - src/qt/res/icons/icons_native/lock_open.svg | 1 - src/qt/res/icons/icons_native/overview.svg | 1 - src/qt/res/icons/icons_native/receive.svg | 1 - src/qt/res/icons/icons_native/send.svg | 1 - .../res/icons/icons_native/transactions.svg | 1 - src/qt/res/icons/notsynced.svg | 86 -------- src/qt/res/icons/staking_off.svg | 132 ------------ src/qt/res/icons/staking_on.svg | 110 ---------- src/qt/res/icons/staking_unable.svg | 132 ------------ src/qt/res/icons/tx_contract_beacon.svg | 7 + .../Voting.svg => tx_contract_voting.svg} | 0 src/qt/res/stylesheets/native_stylesheet.qss | 188 ------------------ src/qt/transactiontablemodel.cpp | 4 +- 27 files changed, 28 insertions(+), 1192 deletions(-) delete mode 100755 src/qt/res/icons/beacon_green.svg delete mode 100755 src/qt/res/icons/beacon_grey.svg delete mode 100755 src/qt/res/icons/beacon_red.svg delete mode 100755 src/qt/res/icons/beacon_yellow.svg delete mode 100755 src/qt/res/icons/gray_scraper.svg delete mode 100755 src/qt/res/icons/green_check.svg delete mode 100755 src/qt/res/icons/green_scraper.svg delete mode 100644 src/qt/res/icons/icons_native/address-book.svg delete mode 100644 src/qt/res/icons/icons_native/lock_closed.svg delete mode 100644 src/qt/res/icons/icons_native/lock_open.svg delete mode 100644 src/qt/res/icons/icons_native/overview.svg delete mode 100644 src/qt/res/icons/icons_native/receive.svg delete mode 100644 src/qt/res/icons/icons_native/send.svg delete mode 100644 src/qt/res/icons/icons_native/transactions.svg delete mode 100755 src/qt/res/icons/notsynced.svg delete mode 100755 src/qt/res/icons/staking_off.svg delete mode 100755 src/qt/res/icons/staking_on.svg delete mode 100644 src/qt/res/icons/staking_unable.svg create mode 100644 src/qt/res/icons/tx_contract_beacon.svg rename src/qt/res/icons/{icons_native/Voting.svg => tx_contract_voting.svg} (100%) delete mode 100644 src/qt/res/stylesheets/native_stylesheet.qss diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 640cbfb740..78cdd390cc 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -307,26 +307,20 @@ RES_ICONS = \ qt/res/icons/ex.png \ qt/res/icons/export.png \ qt/res/icons/filesave.png \ - qt/res/icons/gray_scraper.svg \ - qt/res/icons/green_check.svg \ - qt/res/icons/green_scraper.svg \ qt/res/icons/gridcoin.ico \ qt/res/icons/gridcoin_testnet.ico \ qt/res/icons/key.png \ qt/res/icons/message.svg \ - qt/res/icons/notsynced.svg \ qt/res/icons/qrcode.png \ qt/res/icons/quit.png \ qt/res/icons/remove.png \ qt/res/icons/round_gray_x.svg \ qt/res/icons/round_green_check.svg \ - qt/res/icons/staking_off.svg \ - qt/res/icons/staking_on.svg \ - qt/res/icons/staking_unable.svg \ qt/res/icons/superblock.svg \ qt/res/icons/transaction_conflicted.png \ qt/res/icons/transaction0.png \ qt/res/icons/transaction2.svg \ + qt/res/icons/tx_contract_beacon.svg \ qt/res/icons/tx_contract_voting.svg \ qt/res/icons/tx_inout.svg \ qt/res/icons/tx_input.svg \ @@ -340,13 +334,6 @@ RES_ICONS = \ qt/res/icons/warning.svg \ qt/res/icons/white_and_red_x.svg \ qt/res/icons/www.png \ - qt/res/icons/icons_native/overview.svg \ - qt/res/icons/icons_native/Voting.svg \ - qt/res/icons/icons_native/address-book.svg \ - qt/res/icons/icons_native/lock_closed.svg \ - qt/res/icons/icons_native/lock_open.svg \ - qt/res/icons/icons_native/receive.svg \ - qt/res/icons/icons_native/send.svg \ qt/res/icons/icons_light/sidebar_favorites_active.svg \ qt/res/icons/icons_light/sidebar_favorites_inactive.svg \ qt/res/icons/icons_light/sidebar_history_active.svg \ @@ -425,7 +412,6 @@ RES_ICONS = \ qt/res/icons/icons_dark/status_sync_syncing.svg \ qt/res/icons/icons_dark/transactions.svg \ qt/res/icons/icons_light/transactions.svg \ - qt/res/icons/icons_native/transactions.svg \ qt/res/icons/icons_light/chevron_down.svg \ qt/res/icons/icons_light/chevron_up.svg \ qt/res/icons/icons_dark/chevron_down.svg \ @@ -453,7 +439,6 @@ RES_FONTS = \ RES_STYLESHEETS = \ qt/res/stylesheets/light_stylesheet.qss \ - qt/res/stylesheets/native_stylesheet.qss \ qt/res/stylesheets/dark_stylesheet.qss diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 663c8f557c..ce952cc27b 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -149,24 +149,9 @@ res/icons/icons_dark/chevron_down.svg res/icons/icons_dark/chevron_up.svg - - - res/icons/icons_native/address-book.svg - res/icons/icons_native/transactions.svg - res/icons/icons_native/lock_closed.svg - res/icons/icons_native/lock_open.svg - res/icons/icons_native/overview.svg - res/icons/icons_native/receive.svg - res/icons/icons_native/send.svg - res/icons/icons_native/Voting.svg - res/icons/tx_pos_ss.svg res/icons/tx_por.svg res/icons/tx_por_ss.svg - res/icons/gray_scraper.svg - res/icons/green_scraper.svg res/icons/white_and_red_x.svg res/icons/superblock.svg res/icons/warning.svg @@ -174,6 +159,8 @@ res/icons/round_gray_x.svg res/icons/tx_pos_ss_sent.svg res/icons/tx_por_ss_sent.svg + res/icons/tx_contract_beacon.svg + res/icons/tx_contract_voting.svg res/icons/message.svg @@ -199,7 +186,6 @@ res/stylesheets/light_stylesheet.qss - res/stylesheets/native_stylesheet.qss res/stylesheets/dark_stylesheet.qss diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index e6e8bfd883..8a36fcaa55 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -67,8 +67,8 @@
- - :/icons/address-book:/icons/address-book + + :/icons/address-book_light:/icons/address-book_light Alt+A @@ -265,8 +265,8 @@ - - :/icons/address-book:/icons/address-book + + :/icons/address-book_light:/icons/address-book_light Alt+A diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 85745a860b..b9c31b80d7 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -75,7 +75,6 @@ OptionsDialog::OptionsDialog(QWidget *parent) : ui->styleComboBox->addItem(tr("Light"),QVariant("light")); ui->styleComboBox->addItem(tr("Dark"),QVariant("dark")); - ui->styleComboBox->addItem(tr("Native"),QVariant("native")); /* Widget-to-option mapper */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 840539641e..774e0c4d24 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -123,7 +123,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant((qint64) nReserveBalance); case DisplayUnit: return QVariant(nDisplayUnit); - case DisplayAddresses: + case DisplayAddresses: return QVariant(bDisplayAddresses); case Language: return settings.value("language", ""); @@ -232,9 +232,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("nDisplayUnit", nDisplayUnit); emit displayUnitChanged(nDisplayUnit); break; - case DisplayAddresses: - bDisplayAddresses = value.toBool(); - settings.setValue("bDisplayAddresses", bDisplayAddresses); + case DisplayAddresses: + bDisplayAddresses = value.toBool(); + settings.setValue("bDisplayAddresses", bDisplayAddresses); break; case Language: settings.setValue("language", value); @@ -339,13 +339,18 @@ int OptionsModel::getDisplayUnit() return nDisplayUnit; } -bool OptionsModel::getDisplayAddresses() -{ - return bDisplayAddresses; +bool OptionsModel::getDisplayAddresses() +{ + return bDisplayAddresses; } QString OptionsModel::getCurrentStyle() { + // Native stylesheet removed for now: + if (walletStylesheet == "native") { + return "light"; + } + return walletStylesheet; } diff --git a/src/qt/res/icons/beacon_green.svg b/src/qt/res/icons/beacon_green.svg deleted file mode 100755 index 3e984eeadd..0000000000 --- a/src/qt/res/icons/beacon_green.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/beacon_grey.svg b/src/qt/res/icons/beacon_grey.svg deleted file mode 100755 index b6497992f0..0000000000 --- a/src/qt/res/icons/beacon_grey.svg +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/beacon_red.svg b/src/qt/res/icons/beacon_red.svg deleted file mode 100755 index 53863edbc5..0000000000 --- a/src/qt/res/icons/beacon_red.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/beacon_yellow.svg b/src/qt/res/icons/beacon_yellow.svg deleted file mode 100755 index ca09fe0cd4..0000000000 --- a/src/qt/res/icons/beacon_yellow.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/gray_scraper.svg b/src/qt/res/icons/gray_scraper.svg deleted file mode 100755 index 1fcfec46a0..0000000000 --- a/src/qt/res/icons/gray_scraper.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/src/qt/res/icons/green_check.svg b/src/qt/res/icons/green_check.svg deleted file mode 100755 index befa76b85c..0000000000 --- a/src/qt/res/icons/green_check.svg +++ /dev/null @@ -1,55 +0,0 @@ - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/green_scraper.svg b/src/qt/res/icons/green_scraper.svg deleted file mode 100755 index 4a2d9cc927..0000000000 --- a/src/qt/res/icons/green_scraper.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - diff --git a/src/qt/res/icons/icons_native/address-book.svg b/src/qt/res/icons/icons_native/address-book.svg deleted file mode 100644 index 1938e43e40..0000000000 --- a/src/qt/res/icons/icons_native/address-book.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/icons_native/lock_closed.svg b/src/qt/res/icons/icons_native/lock_closed.svg deleted file mode 100644 index 728bb2ac43..0000000000 --- a/src/qt/res/icons/icons_native/lock_closed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/icons_native/lock_open.svg b/src/qt/res/icons/icons_native/lock_open.svg deleted file mode 100644 index 2cda9a38eb..0000000000 --- a/src/qt/res/icons/icons_native/lock_open.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/icons_native/overview.svg b/src/qt/res/icons/icons_native/overview.svg deleted file mode 100644 index ddbfd8a1e9..0000000000 --- a/src/qt/res/icons/icons_native/overview.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/icons_native/receive.svg b/src/qt/res/icons/icons_native/receive.svg deleted file mode 100644 index 423917e1f6..0000000000 --- a/src/qt/res/icons/icons_native/receive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/icons_native/send.svg b/src/qt/res/icons/icons_native/send.svg deleted file mode 100644 index 0642a1e780..0000000000 --- a/src/qt/res/icons/icons_native/send.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/icons_native/transactions.svg b/src/qt/res/icons/icons_native/transactions.svg deleted file mode 100644 index 12b2992f23..0000000000 --- a/src/qt/res/icons/icons_native/transactions.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/qt/res/icons/notsynced.svg b/src/qt/res/icons/notsynced.svg deleted file mode 100755 index 8d3f17a5a7..0000000000 --- a/src/qt/res/icons/notsynced.svg +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/src/qt/res/icons/staking_off.svg b/src/qt/res/icons/staking_off.svg deleted file mode 100755 index 014225057f..0000000000 --- a/src/qt/res/icons/staking_off.svg +++ /dev/null @@ -1,132 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/qt/res/icons/staking_on.svg b/src/qt/res/icons/staking_on.svg deleted file mode 100755 index 85e7374ea1..0000000000 --- a/src/qt/res/icons/staking_on.svg +++ /dev/null @@ -1,110 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/qt/res/icons/staking_unable.svg b/src/qt/res/icons/staking_unable.svg deleted file mode 100644 index 8cbd8bc5e2..0000000000 --- a/src/qt/res/icons/staking_unable.svg +++ /dev/null @@ -1,132 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/qt/res/icons/tx_contract_beacon.svg b/src/qt/res/icons/tx_contract_beacon.svg new file mode 100644 index 0000000000..b1a0095a1d --- /dev/null +++ b/src/qt/res/icons/tx_contract_beacon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/qt/res/icons/icons_native/Voting.svg b/src/qt/res/icons/tx_contract_voting.svg similarity index 100% rename from src/qt/res/icons/icons_native/Voting.svg rename to src/qt/res/icons/tx_contract_voting.svg diff --git a/src/qt/res/stylesheets/native_stylesheet.qss b/src/qt/res/stylesheets/native_stylesheet.qss deleted file mode 100644 index 9cb3a70753..0000000000 --- a/src/qt/res/stylesheets/native_stylesheet.qss +++ /dev/null @@ -1,188 +0,0 @@ -/* general */ - -* { - font-family: "Inter"; -} - -QMainWindow { - font-family: "Inter"; -} - -QMenuBar { - background: rgb(240,240,240); - color: black; -} - -QMenuBar::item { - border: 0; - padding-bottom: 0.25em; - padding-top: 0.25em; - padding-left: 1em; - padding-right: 1em; - color: black; - background-color: rgb(240,240,240); -} - -QTextEdit { - background-color: white; -} - -/* RPC Console */ -#messagesWidget, #lineEdit, #scraper_log { - font-family: "Inconsolata"; -} - -/* Main Window*/ - -#toolbar { - border:none; - padding-top:1.25em; - min-width:8.5em; - height:100%; -} - -#toolbar2 { - border:none; - min-width:1.625em; - max-width:3em; -} - -QToolBar#toolbar QToolButton { - padding-left:1.25em; - padding-right:1.25em; - padding-top:0.5em; - padding-bottom:0.5em; - margin:0.25em; - width:100%; -} - -QToolBar#toolbar2 QLabel{ - border:none; - padding-top:1em; - padding-bottom:1em; - qproperty-alignment: AlignCenter; -} - -#spacer { - background-color:transparent; - border:none; -} - -#listTransactions{ - background-color:transparent; -} - -#boincLogo{ - image:url(:/images/boinc_w); - min-width:5.38em; - max-width:5.38em; - min-height:2.25em; - max-height:2.25em; -} - -#gridcoinLogoHorizontal{ - image:url(:/images/logo_hz); - min-width:9.225em; - max-width:9.225em; - min-height:2.25em; - max-height:2.25em; -} - -#aboutLogoLabel{ - image:url(:/images/about_light); - min-width:2.25em; - max-width:2.25em; - min-height:9.225em; - max-height:9.225em; -} - -/* overview page */ - -#overviewWalletLabel, -#researcherHeaderLabel, -#stakingHeaderLabel, -#recentTransLabel { - font-size:15pt; - font-weight:bold; -} - -#availableLabel, -#immatureTextLabel, -#totalBalanceLabel { - font-weight:bold; -} - -#walletStatusLabel, -#transactionsStatusLabel, -#researcherAlertLabel { - color: red; -} - -/* coincontrol dialog*/ - -#coinControlFeaturesLabel{ - font-weight:bold; -} - -#coinControlInsuffFundsLabel{ - color:red; - font-weight:bold; -} - -#coinControlBytesTextLabel{ - font-weight:bold; -} - -#coinControlQuantityTextLabel{ - font-weight:bold; -} - -#coinControlAmountTextLabel{ - font-weight:bold; -} - -#coinControlPriorityTextLabel{ - font-weight:bold; -} - -#coinControlFeeTextLabel{ - font-weight:bold; -} - -#coinControlLowOutputTextLabel{ - font-weight:bold; -} - -#coinControlAfterFeeTextLabel{ - font-weight:bold; -} - -#coinControlChangeTextLabel{ - font-weight:bold; -} - -/* options dialog */ - -#OptionsDialog #statusLabel{ - font-weight:bold; -} - -/* sign verify message dialog */ - -#signatureOut_SM{ - font-style: italic; -} - -#statusLabel_SM{ - font-weight:bold; -} - -#statusLabel_VM{ - font-weight:bold; -} - -/* ask passphrase dialog */ - -#capsLabel{ - font-weight:bold; -} diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 861b8697c9..3692ae5a2c 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -476,10 +476,10 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx case TransactionRecord::SendToOther: return QIcon(":/icons/tx_output"); case TransactionRecord::BeaconAdvertisement: - return QIcon(":/icons/beacon_grey"); + return QIcon(":/icons/tx_contract_beacon"); case TransactionRecord::Poll: case TransactionRecord::Vote: - return QIcon(":/icons/voting_native"); + return QIcon(":/icons/tx_contract_voting"); case TransactionRecord::Message: return QIcon(":/icons/message"); default: From cc1d0db4efdf35680ce17147009eab0bcb7b2dfd Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:18:00 -0500 Subject: [PATCH 038/280] Adjust miscellaneous GUI layout and spacing issues --- src/qt/bitcoingui.cpp | 29 ++++++++------ src/qt/bitcoingui.h | 6 ++- src/qt/forms/overviewpage.ui | 42 ++++++++++----------- src/qt/forms/researcherwizardsummarypage.ui | 12 +++--- src/qt/forms/signverifymessagedialog.ui | 26 ++++--------- src/qt/overviewpage.cpp | 1 - src/qt/res/stylesheets/dark_stylesheet.qss | 4 +- src/qt/res/stylesheets/light_stylesheet.qss | 4 +- src/qt/sendcoinsentry.cpp | 3 +- src/qt/transactionview.cpp | 14 ++----- src/qt/transactionview.h | 4 +- src/qt/votingdialog.cpp | 5 +-- 12 files changed, 68 insertions(+), 82 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d515ccf3e7..728b416528 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -178,9 +178,17 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): vbox->addWidget(transactionView); transactionsPage->setLayout(vbox); - addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); - - receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); + addressBookPage = new QWidget(this); + addressBook = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); + QVBoxLayout *addressBookPageLayout = new QVBoxLayout(); + addressBookPageLayout->addWidget(addressBook); + addressBookPage->setLayout(addressBookPageLayout); + + receiveCoinsPage = new QWidget(this); + receiveAddressBook = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); + QVBoxLayout *receiveCoinsPageLayout = new QVBoxLayout(); + receiveCoinsPageLayout->addWidget(receiveAddressBook); + receiveCoinsPage->setLayout(receiveCoinsPageLayout); sendCoinsPage = new SendCoinsDialog(this); @@ -510,14 +518,13 @@ void BitcoinGUI::createMenuBar() file->addAction(exportAction); file->addAction(signMessageAction); file->addAction(verifyMessageAction); + file->addSeparator(); if (!GetBoolArg("-testnet", false)) { - file->addSeparator(); file->addAction(snapshotAction); } - file->addSeparator(); file->addAction(resetblockchainAction); file->addSeparator(); @@ -679,8 +686,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); rpcConsole->setClientModel(clientModel); - addressBookPage->setOptionsModel(clientModel->getOptionsModel()); - receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); + addressBook->setOptionsModel(clientModel->getOptionsModel()); + receiveAddressBook->setOptionsModel(clientModel->getOptionsModel()); } } @@ -700,8 +707,8 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) transactionView->setModel(walletModel); overviewPage->setWalletModel(walletModel); - addressBookPage->setModel(walletModel->getAddressTableModel()); - receiveCoinsPage->setModel(walletModel->getAddressTableModel()); + addressBook->setModel(walletModel->getAddressTableModel()); + receiveAddressBook->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); votingPage->setModel(walletModel); signVerifyMessageDialog->setModel(walletModel); @@ -1205,7 +1212,7 @@ void BitcoinGUI::gotoAddressBookPage() exportAction->setEnabled(true); disconnect(exportAction, SIGNAL(triggered()), 0, 0); - connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked())); + connect(exportAction, SIGNAL(triggered()), addressBook, SLOT(exportClicked())); } void BitcoinGUI::gotoReceiveCoinsPage() @@ -1215,7 +1222,7 @@ void BitcoinGUI::gotoReceiveCoinsPage() exportAction->setEnabled(true); disconnect(exportAction, SIGNAL(triggered()), 0, 0); - connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked())); + connect(exportAction, SIGNAL(triggered()), receiveAddressBook, SLOT(exportClicked())); } void BitcoinGUI::gotoSendCoinsPage() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index bbdc68a8b9..f9a32b5110 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -79,8 +79,8 @@ class BitcoinGUI : public QMainWindow OverviewPage *overviewPage; QWidget *transactionsPage; - AddressBookPage *addressBookPage; - AddressBookPage *receiveCoinsPage; + QWidget *addressBookPage; + QWidget *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; VotingDialog *votingPage; SignVerifyMessageDialog *signVerifyMessageDialog; @@ -128,6 +128,8 @@ class BitcoinGUI : public QMainWindow QMenu *trayIconMenu; Notificator *notificator; TransactionView *transactionView; + AddressBookPage *addressBook; + AddressBookPage *receiveAddressBook; RPCConsole *rpcConsole; DiagnosticsDialog *diagnosticsDialog; diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 9a27155c90..319cf0007b 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -39,10 +39,10 @@ 6 - + - + 0 0 @@ -535,7 +535,7 @@ - :/icons/warning + :/icons/warning true @@ -576,25 +576,25 @@ + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 10000 + + + + - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 20 - - - - @@ -775,8 +775,6 @@
clicklabel.h
- - - + diff --git a/src/qt/forms/researcherwizardsummarypage.ui b/src/qt/forms/researcherwizardsummarypage.ui index b4bb8b718e..d4e744588e 100644 --- a/src/qt/forms/researcherwizardsummarypage.ui +++ b/src/qt/forms/researcherwizardsummarypage.ui @@ -65,7 +65,7 @@ 20 - 20 + 10 @@ -152,7 +152,7 @@
- :/icons/round_green_check + :/icons/round_green_check true @@ -281,7 +281,7 @@ - :/images/ic_solo_active + :/images/ic_solo_active true @@ -787,7 +787,7 @@ - :/icons/white_and_red_x + :/icons/white_and_red_x true @@ -890,8 +890,6 @@ - - - + diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 8a36fcaa55..bbd86e4b67 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -43,7 +43,7 @@ - 0 + 5 @@ -59,7 +59,7 @@ - + Choose an address from the address book @@ -73,13 +73,10 @@ Alt+A - - false - - + Paste address from clipboard @@ -93,9 +90,6 @@ Alt+P - - false - @@ -110,7 +104,7 @@ - 0 + 5 @@ -123,7 +117,7 @@ - + Copy the current signature to the system clipboard @@ -134,9 +128,6 @@ :/icons/editcopy:/icons/editcopy - - false - @@ -241,7 +232,7 @@ - 0 + 5 @@ -257,7 +248,7 @@ - + Choose an address from the address book @@ -271,9 +262,6 @@ Alt+A - - false - diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 533cc290d4..265a39febb 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -128,7 +128,6 @@ OverviewPage::OverviewPage(QWidget *parent) : QRect verticalSpacerSpacing(0, 0, 20, 20 * this->logicalDpiY() / 96); ui->verticalSpacer->setGeometry(verticalSpacerSpacing); ui->researcherSectionVerticalSpacer->setGeometry(verticalSpacerSpacing); - ui->verticalSpacer_2->setGeometry(verticalSpacerSpacing); // Recent transactions ui->listTransactions->setItemDelegate(txdelegate); diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 41e7b746eb..2aba02ed78 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -35,9 +35,11 @@ QTreeWidget { } .QFrame, +AddressBookPage, QGroupBox, QTabWidget::pane, -#SendCoinsEntry { +#SendCoinsEntry, +TransactionView { background: rgb(23, 30, 40); border: none; border-radius: 0.26em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 8f6d06eba0..8d597c372f 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -31,9 +31,11 @@ QTreeWidget { } .QFrame, +AddressBookPage, QGroupBox, QTabWidget::pane, -#SendCoinsEntry { +#SendCoinsEntry, +TransactionView { background: white; border: none; border-radius: 0.26em; diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 3fa908274b..ddc5fa1198 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -17,9 +17,8 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) : { ui->setupUi(this); -#ifdef Q_OS_MAC ui->payToLayout->setSpacing(4); -#endif + setFocusPolicy(Qt::TabFocus); setFocusProxy(ui->payTo); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index a5dcadd35f..429ad9b6f6 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -30,7 +30,7 @@ #include TransactionView::TransactionView(QWidget *parent) : - QWidget(parent), model(0), transactionProxyModel(0), + QFrame(parent), model(0), transactionProxyModel(0), transactionView(0) { // Build filter row @@ -38,13 +38,8 @@ TransactionView::TransactionView(QWidget *parent) : QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0,0,0,0); -#ifdef Q_OS_MAC hlayout->setSpacing(5); hlayout->addSpacing(26); -#else - hlayout->setSpacing(0); - hlayout->addSpacing(23); -#endif dateWidget = new QComboBox(this); #ifdef Q_OS_MAC @@ -52,7 +47,7 @@ TransactionView::TransactionView(QWidget *parent) : #else dateWidget->setFixedWidth(120); #endif - dateWidget->addItem(tr("All"), All); + dateWidget->addItem(tr("All Time"), All); dateWidget->addItem(tr("Today"), Today); dateWidget->addItem(tr("This week"), ThisWeek); dateWidget->addItem(tr("This month"), ThisMonth); @@ -68,7 +63,7 @@ TransactionView::TransactionView(QWidget *parent) : typeWidget->setFixedWidth(120); #endif - typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES); + typeWidget->addItem(tr("All Types"), TransactionFilterProxy::ALL_TYPES); typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) | TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther)); typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | @@ -98,13 +93,12 @@ TransactionView::TransactionView(QWidget *parent) : QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); - vlayout->setSpacing(0); QTableView *view = new QTableView(this); vlayout->addLayout(hlayout); vlayout->addWidget(createDateRangeWidget()); vlayout->addWidget(view); - vlayout->setSpacing(0); + vlayout->setSpacing(5); int width = view->verticalScrollBar()->sizeHint().width(); // Cover scroll bar width with spacing #ifdef Q_OS_MAC diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 81b4f056e0..a256c8c63e 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -1,7 +1,7 @@ #ifndef TRANSACTIONVIEW_H #define TRANSACTIONVIEW_H -#include +#include class WalletModel; class TransactionFilterProxy; @@ -19,7 +19,7 @@ QT_END_NAMESPACE /** Widget showing the transaction list for a wallet, including a filter row. Using the filter row, the user can view or export a subset of the transactions. */ -class TransactionView : public QWidget +class TransactionView : public QFrame { Q_OBJECT public: diff --git a/src/qt/votingdialog.cpp b/src/qt/votingdialog.cpp index 628adc2961..52c1504e95 100644 --- a/src/qt/votingdialog.cpp +++ b/src/qt/votingdialog.cpp @@ -392,19 +392,16 @@ VotingDialog::VotingDialog(QWidget *parent) QPushButton *resetButton = new QPushButton(); resetButton->setText(tr("Reload Polls")); - resetButton->setMaximumWidth(150); groupboxhlayout->addWidget(resetButton); connect(resetButton, SIGNAL(clicked()), this, SLOT(resetData())); QPushButton *histButton = new QPushButton(); histButton->setText(tr("Load History")); - histButton->setMaximumWidth(150); groupboxhlayout->addWidget(histButton); connect(histButton, SIGNAL(clicked()), this, SLOT(loadHistory())); QPushButton *newPollButton = new QPushButton(); newPollButton->setText(tr("Create Poll")); - newPollButton->setMaximumWidth(150); groupboxhlayout->addWidget(newPollButton); connect(newPollButton, SIGNAL(clicked()), this, SLOT(showNewPollDialog())); @@ -997,7 +994,7 @@ NewPollDialog::NewPollDialog(QWidget *parent) QGridLayout *glayout = new QGridLayout(); glayout->setHorizontalSpacing(0); - glayout->setVerticalSpacing(0); + glayout->setVerticalSpacing(5); glayout->setColumnStretch(0, 1); glayout->setColumnStretch(1, 3); glayout->setColumnStretch(2, 5); From 96a4ceb9e4e22fc327c7a6c64df0a2ed3e6eef05 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:18:05 -0500 Subject: [PATCH 039/280] Highlight GUI statusbar icon when fully-unlocked This causes the GUI to display a red, open padlock when the wallet is fully-unlocked (not for staking only) to remind a user to lock it. --- src/qt/bitcoingui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 728b416528..a570a765c5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1316,7 +1316,11 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: - labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_unlocked_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + if (fWalletUnlockStakingOnly) { + labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_unlocked_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + } else { + labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_none_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + } labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently %1 ").arg(fWalletUnlockStakingOnly ? tr("unlocked for staking only") : tr("fully unlocked"))); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); From 44dfc334a0f753e3a314923c14e47e6353ffc8e9 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 12 Apr 2021 12:19:22 -0500 Subject: [PATCH 040/280] Move GUI menu bar over content layout This changes the position of the main menu bar to stack above the main content widget. It saves a bit of space and avoids the color valley above the toolbar to balance the light theme appearance. --- src/qt/bitcoingui.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a570a765c5..22decd4543 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -196,6 +196,11 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): signVerifyMessageDialog = new SignVerifyMessageDialog(this); + QVBoxLayout *centralVbox = new QVBoxLayout(this); + centralVbox->setContentsMargins(0, 0, 0, 0); + centralVbox->setSpacing(0); + centralVbox->addWidget(appMenuBar); + centralWidget = new QStackedWidget(this); centralWidget->addWidget(overviewPage); centralWidget->addWidget(transactionsPage); @@ -203,7 +208,11 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(sendCoinsPage); centralWidget->addWidget(votingPage); - setCentralWidget(centralWidget); + centralVbox->addWidget(centralWidget); + + QWidget* centralWidgetWrapper = new QWidget(this); + centralWidgetWrapper->setLayout(centralVbox); + setCentralWidget(centralWidgetWrapper); // Create status bar statusBar(); From 97ea1a3b949bf3307acce96b68566fca22a1ee50 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sat, 17 Apr 2021 07:49:02 -0500 Subject: [PATCH 041/280] Change GUI default theme to "dark" --- src/qt/optionsdialog.cpp | 4 ++-- src/qt/optionsmodel.cpp | 6 +++--- src/qt/researcher/researchermodel.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b9c31b80d7..c2b582065e 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -73,8 +73,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) : ui->unit->setModel(new BitcoinUnits(this)); - ui->styleComboBox->addItem(tr("Light"),QVariant("light")); - ui->styleComboBox->addItem(tr("Dark"),QVariant("dark")); + ui->styleComboBox->addItem(tr("Dark"), QVariant("dark")); + ui->styleComboBox->addItem(tr("Light"), QVariant("light")); /* Widget-to-option mapper */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 774e0c4d24..a75287463d 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -53,7 +53,7 @@ void OptionsModel::Init() limitTxnDate = settings.value("limitTxnDate", QDate()).toDate(); nReserveBalance = settings.value("nReserveBalance").toLongLong(); language = settings.value("language", "").toString(); - walletStylesheet = settings.value("walletStylesheet", "light").toString(); + walletStylesheet = settings.value("walletStylesheet", "dark").toString(); // These are shared with core Bitcoin; we want // command-line options to override the GUI settings: @@ -128,7 +128,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const case Language: return settings.value("language", ""); case WalletStylesheet: - return settings.value("walletStylesheet", "light"); + return settings.value("walletStylesheet", "dark"); case CoinControlFeatures: return QVariant(fCoinControlFeatures); case LimitTxnDisplay: @@ -348,7 +348,7 @@ QString OptionsModel::getCurrentStyle() { // Native stylesheet removed for now: if (walletStylesheet == "native") { - return "light"; + return "dark"; } return walletStylesheet; diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 08e119cdac..5708cf1d54 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -87,7 +87,7 @@ ResearcherModel::ResearcherModel() , m_configured_for_investor_mode(false) , m_wizard_open(false) , m_out_of_sync(true) - , m_theme_suffix("_light") + , m_theme_suffix("_dark") { qRegisterMetaType("GRC::ResearcherPtr"); From 7ff42da7d5228e84edfc5fa09d66dfd2dabab651 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sat, 17 Apr 2021 07:51:08 -0500 Subject: [PATCH 042/280] Add BOINC logo to GUI toolbar --- src/qt/bitcoingui.cpp | 10 +++ src/qt/res/images/boinc_logo_white.svg | 96 ++------------------- src/qt/res/stylesheets/dark_stylesheet.qss | 15 ++-- src/qt/res/stylesheets/light_stylesheet.qss | 15 ++-- 4 files changed, 33 insertions(+), 103 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 22decd4543..566d1f0524 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -573,6 +573,13 @@ void BitcoinGUI::createToolBars() logoLabel->setObjectName("toolbarLogoLabel"); connect(logoLabel, SIGNAL(clicked()), this, SLOT(websiteClicked())); + QWidget *boincLabelSpacer = new QWidget(); + boincLabelSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + ClickLabel *boincLabel = new ClickLabel(); + boincLabel->setObjectName("toolbarBoincLabel"); + connect(logoLabel, SIGNAL(clicked()), this, SLOT(boincClicked())); + // "Tabs" toolbar (vertical, aligned on left side of overview screen). QToolBar *toolbar = addToolBar("Tabs toolbar"); toolbar->setObjectName("toolbar"); @@ -594,6 +601,9 @@ void BitcoinGUI::createToolBars() toolbar->addSeparator(); toolbar->addAction(unlockWalletAction); toolbar->addAction(lockWalletAction); + toolbar->addWidget(boincLabelSpacer); + toolbar->addWidget(boincLabel); + toolbar->layout()->setAlignment(boincLabel, Qt::AlignHCenter | Qt::AlignBottom); addToolBarBreak(Qt::LeftToolBarArea); diff --git a/src/qt/res/images/boinc_logo_white.svg b/src/qt/res/images/boinc_logo_white.svg index 6a6a68c68c..a5ee85273d 100644 --- a/src/qt/res/images/boinc_logo_white.svg +++ b/src/qt/res/images/boinc_logo_white.svg @@ -1,88 +1,10 @@ - - -image/svg+xml - - - - - - - - - + + + + + + + + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 2aba02ed78..a8c8d2d26e 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -455,6 +455,13 @@ QToolBar#toolbar::separator { padding: 1em 1.5em; } +#toolbarBoincLabel { + image: url(:/images/boinc_w); + min-width: 4em; + min-height: 1.68em; + padding: 1em; +} + QStatusBar { background: rgb(23, 23, 23); border-top: 0.065em solid rgb(19, 19, 19); @@ -487,14 +494,6 @@ QStatusBar QToolTip { font-weight: bold; } -#boincLogo{ - image:url(:/images/boinc_w); - min-width:5.38em; - max-width:5.38em; - min-height:2.25em; - max-height:2.25em; -} - #aboutLogoLabel{ image:url(:/images/about); min-width:2.25em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 8d597c372f..b4d9569055 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -468,6 +468,13 @@ QToolBar#toolbar::separator { padding: 1em 1.5em; } +#toolbarBoincLabel { + image: url(:/images/boinc_w); + min-width: 4em; + min-height: 1.68em; + padding: 1em; +} + QStatusBar { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(243, 243, 243), stop: 1 rgb(212, 212, 212)); border-top: 0.065em solid rgb(230, 230, 230); @@ -496,14 +503,6 @@ QStatusBar .QFrame QLabel { font-weight: bold; } -#boincLogo{ - image:url(:/images/boinc_w); - min-width:5.38em; - max-width:5.38em; - min-height:2.25em; - max-height:2.25em; -} - #aboutLogoLabel{ image:url(:/images/about_light); min-width:2.25em; From 8372d777ee2e6bf4585bbc6abe5335986b7c2d34 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 02:47:51 -0500 Subject: [PATCH 043/280] Set explicit base font size for macOS This fixes the rendered size for widgets that have a smaller or larger font size defined by Qt's macOS style. In particular, it increases the text size of QToolButton widgets in the sidebar to improve legibility. Since macOS uses a different base OS-level DPI than Windows and Linux, the applicaton needs a larger base point size when rendering for macOS desktops. --- src/qt/bitcoingui.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 566d1f0524..1d571fbb3f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -112,16 +112,20 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QFontDatabase::addApplicationFont(":/fonts/inter-variable"); QFontDatabase::addApplicationFont(":/fonts/inconsolata-regular"); -#ifndef Q_OS_MAC // This slightly enlarges the application's base font size on Windows and - // Linux. MacOS often uses a different reference DPI so this can actually - // cause the rendered text to appear smaller. On Mac, the default size is - // adequate. + // Linux. MacOS often uses a different reference DPI so the size used for + // Windows and Linux causes the rendered text to appear smaller. For Mac, + // we set an explicit application-wide font size to Qt's default value to + // normalize text size on controls like toolbar buttons and tabs that can + // render smaller with the Cocoa integration plugin: // QFont appFont = qApp->font(); +#ifndef Q_OS_MAC appFont.setPointSize(10); - qApp->setFont(appFont); +#else + appFont.setPointSize(13); #endif + qApp->setFont(appFont); // Qt paints some decorations directly and provides no stylesheet hooks to // customize appearance (for example: the rulers in a QWizardPage). We set From 08988387d0518d3d9114df7cc92a488debec8017 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 04:37:08 -0500 Subject: [PATCH 044/280] Scale GUI designer/stylesheet font sizes for macOS This fixes legibility issues with text on macOS by scaling font sizes defined in point units. Macs use a different base DPI so converting a point size to pixels results in smaller text. --- src/Makefile.qt.include | 2 + src/qt/decoration.cpp | 56 +++++++++++++++++++ src/qt/decoration.h | 29 ++++++++++ src/qt/diagnosticsdialog.cpp | 5 ++ src/qt/forms/diagnosticsdialog.ui | 3 - src/qt/forms/researcherwizardauthpage.ui | 1 - src/qt/forms/researcherwizardinvestorpage.ui | 5 -- .../forms/researcherwizardmodedetailpage.ui | 5 -- src/qt/forms/researcherwizardmodepage.ui | 5 -- src/qt/forms/researcherwizardpoolpage.ui | 5 -- .../forms/researcherwizardpoolsummarypage.ui | 5 -- src/qt/forms/researcherwizardsummarypage.ui | 6 -- src/qt/forms/rpcconsole.ui | 15 ----- src/qt/overviewpage.cpp | 6 ++ src/qt/res/stylesheets/dark_stylesheet.qss | 1 - src/qt/res/stylesheets/light_stylesheet.qss | 1 - .../researcher/researcherwizardauthpage.cpp | 3 + .../researcherwizardinvestorpage.cpp | 3 + .../researcherwizardmodedetailpage.cpp | 3 + .../researcher/researcherwizardmodepage.cpp | 3 + .../researcher/researcherwizardpoolpage.cpp | 3 + .../researcherwizardpoolsummarypage.cpp | 3 + .../researcherwizardsummarypage.cpp | 4 ++ src/qt/rpcconsole.cpp | 3 + 24 files changed, 123 insertions(+), 52 deletions(-) create mode 100644 src/qt/decoration.cpp create mode 100644 src/qt/decoration.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 78cdd390cc..bed56b3523 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -182,6 +182,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/coincontroldialog.h \ qt/coincontroltreewidget.h \ qt/csvmodelwriter.h \ + qt/decoration.h \ qt/diagnosticsdialog.h \ qt/editaddressdialog.h \ qt/guiconstants.h \ @@ -243,6 +244,7 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/coincontroldialog.cpp \ qt/coincontroltreewidget.cpp \ qt/csvmodelwriter.cpp \ + qt/decoration.cpp \ qt/diagnosticsdialog.cpp \ qt/editaddressdialog.cpp \ qt/guiutil.cpp \ diff --git a/src/qt/decoration.cpp b/src/qt/decoration.cpp new file mode 100644 index 0000000000..1e43b39b9c --- /dev/null +++ b/src/qt/decoration.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +using namespace GRC; + +namespace { +//! +//! \brief The pixels-per-inch value that the user interface is designed for. +//! +constexpr int REFERENCE_DPI = 96; + +//! +//! \brief The virtual pixels-per-inch of the operating system. +//! +//! MacOS typically uses a different base DPI than Windows or Linux. This +//! causes rendering calculated from a reference DPI to appear smaller on +//! Macs which creates legibility and layout issues. +//! +#ifdef Q_OS_MAC +constexpr int OS_BASE_DPI = 72; +#else +constexpr int OS_BASE_DPI = 96; +#endif +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Functions +// ----------------------------------------------------------------------------- + +void GRC::ScaleFontPointSize(QWidget* widget, int point_size) +{ + if (!widget) { + return; + } + + QFont font = widget->font(); + font.setPointSize(point_size * REFERENCE_DPI / OS_BASE_DPI); + widget->setFont(font); +} + +void GRC::ScaleFontPointSizeF(QWidget* widget, double point_size) +{ + if (!widget) { + return; + } + + QFont font = widget->font(); + font.setPointSizeF(point_size * REFERENCE_DPI / OS_BASE_DPI); + widget->setFont(font); +} diff --git a/src/qt/decoration.h b/src/qt/decoration.h new file mode 100644 index 0000000000..3b5902f65c --- /dev/null +++ b/src/qt/decoration.h @@ -0,0 +1,29 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace GRC { +//! +//! \brief Set a widget's scaled font size in integer points. +//! +//! This accomodates macOS which scales a point unit with a different base DPI +//! than other operating systems. +//! +void ScaleFontPointSize(QWidget* widget, int point_size); + +//! +//! \brief Set a widget's scaled font size in floating-point points. +//! +//! This accomodates macOS which scales a point unit with a different base DPI +//! than other operating systems. +//! +void ScaleFontPointSizeF(QWidget* widget, double point_size); +} // namespace GRC diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index f17a0f862a..15dbed3f57 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -15,6 +15,7 @@ #include "gridcoin/researcher.h" #include "gridcoin/staking/difficulty.h" #include "gridcoin/upgrade.h" +#include "qt/decoration.h" #include "qt/researcher/researchermodel.h" #include @@ -27,6 +28,10 @@ DiagnosticsDialog::DiagnosticsDialog(QWidget *parent, ResearcherModel* researche m_researcher_model(researcher_model) { ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->diagnosticsLabel, 14); + GRC::ScaleFontPointSize(ui->overallResultLabel, 12); + GRC::ScaleFontPointSize(ui->overallResultResultLabel, 12); } DiagnosticsDialog::~DiagnosticsDialog() diff --git a/src/qt/forms/diagnosticsdialog.ui b/src/qt/forms/diagnosticsdialog.ui index d66d70f94e..f790b288c4 100644 --- a/src/qt/forms/diagnosticsdialog.ui +++ b/src/qt/forms/diagnosticsdialog.ui @@ -67,7 +67,6 @@ - 12 75 true @@ -152,7 +151,6 @@ - 12 75 true @@ -358,7 +356,6 @@ - 14 75 true diff --git a/src/qt/forms/researcherwizardauthpage.ui b/src/qt/forms/researcherwizardauthpage.ui index c4baef6f96..a24d6bd7d5 100644 --- a/src/qt/forms/researcherwizardauthpage.ui +++ b/src/qt/forms/researcherwizardauthpage.ui @@ -111,7 +111,6 @@ Monospace - 10 diff --git a/src/qt/forms/researcherwizardinvestorpage.ui b/src/qt/forms/researcherwizardinvestorpage.ui index a5672d52c5..bad115f044 100644 --- a/src/qt/forms/researcherwizardinvestorpage.ui +++ b/src/qt/forms/researcherwizardinvestorpage.ui @@ -72,11 +72,6 @@ - - - 11 - - Investor Mode diff --git a/src/qt/forms/researcherwizardmodedetailpage.ui b/src/qt/forms/researcherwizardmodedetailpage.ui index 972d49e0e1..419d51a440 100644 --- a/src/qt/forms/researcherwizardmodedetailpage.ui +++ b/src/qt/forms/researcherwizardmodedetailpage.ui @@ -31,11 +31,6 @@ - - - 16 - - How can I participate? diff --git a/src/qt/forms/researcherwizardmodepage.ui b/src/qt/forms/researcherwizardmodepage.ui index 1d83a0b9d4..9d52755a29 100644 --- a/src/qt/forms/researcherwizardmodepage.ui +++ b/src/qt/forms/researcherwizardmodepage.ui @@ -78,11 +78,6 @@ - - - 16 - - How would you like to participate? diff --git a/src/qt/forms/researcherwizardpoolpage.ui b/src/qt/forms/researcherwizardpoolpage.ui index 7fb3aac9be..8fb9782f31 100644 --- a/src/qt/forms/researcherwizardpoolpage.ui +++ b/src/qt/forms/researcherwizardpoolpage.ui @@ -72,11 +72,6 @@ - - - 11 - - Pool Mode diff --git a/src/qt/forms/researcherwizardpoolsummarypage.ui b/src/qt/forms/researcherwizardpoolsummarypage.ui index cc9a84e639..8da0715691 100644 --- a/src/qt/forms/researcherwizardpoolsummarypage.ui +++ b/src/qt/forms/researcherwizardpoolsummarypage.ui @@ -72,11 +72,6 @@ - - - 11 - - Pool Mode diff --git a/src/qt/forms/researcherwizardsummarypage.ui b/src/qt/forms/researcherwizardsummarypage.ui index d4e744588e..ad172eed48 100644 --- a/src/qt/forms/researcherwizardsummarypage.ui +++ b/src/qt/forms/researcherwizardsummarypage.ui @@ -94,11 +94,6 @@ 0 - - - 11 - - IBeamCursor @@ -568,7 +563,6 @@ Monospace - 8 diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index 2b4ff8be2c..53d024f6c3 100755 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -19,11 +19,6 @@ - - - 10 - - false @@ -746,11 +741,6 @@ 32 - - - 12 - - IBeamCursor @@ -820,11 +810,6 @@ 0 - - - 10 - - IBeamCursor diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 265a39febb..67632952c1 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -5,6 +5,7 @@ #include "ui_overviewpage.h" #ifndef Q_MOC_RUN +#include "qt/decoration.h" #include "main.h" #endif #include "researcher/researchermodel.h" @@ -118,6 +119,11 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->setupUi(this); + GRC::ScaleFontPointSize(ui->overviewWalletLabel, 15); + GRC::ScaleFontPointSize(ui->researcherHeaderLabel, 15); + GRC::ScaleFontPointSize(ui->stakingHeaderLabel, 15); + GRC::ScaleFontPointSize(ui->recentTransLabel, 15); + // Override .ui default spacing to deal with various dpi displays. int verticalSpacing = 7 * this->logicalDpiY() / 96; ui->verticalLayout_10->setMargin(verticalSpacing); diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index a8c8d2d26e..c7cb825c2f 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -508,7 +508,6 @@ QStatusBar QToolTip { #researcherHeaderLabel, #stakingHeaderLabel, #recentTransLabel { - font-size: 15pt; font-weight: bold; color: rgb(250, 250, 250); } diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index b4d9569055..b85a956da6 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -517,7 +517,6 @@ QStatusBar .QFrame QLabel { #researcherHeaderLabel, #stakingHeaderLabel, #recentTransLabel { - font-size: 15pt; font-weight: bold; color: rgb(43, 52, 69); } diff --git a/src/qt/researcher/researcherwizardauthpage.cpp b/src/qt/researcher/researcherwizardauthpage.cpp index b753fbf509..99f72b1d43 100644 --- a/src/qt/researcher/researcherwizardauthpage.cpp +++ b/src/qt/researcher/researcherwizardauthpage.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardauthpage.h" #include "qt/researcher/researchermodel.h" #include "qt/researcher/researcherwizardauthpage.h" @@ -18,6 +19,8 @@ ResearcherWizardAuthPage::ResearcherWizardAuthPage(QWidget *parent) , m_researcher_model(nullptr) { ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->verificationCodeLabel, 10); } ResearcherWizardAuthPage::~ResearcherWizardAuthPage() diff --git a/src/qt/researcher/researcherwizardinvestorpage.cpp b/src/qt/researcher/researcherwizardinvestorpage.cpp index fd4f2b097d..808a017e83 100644 --- a/src/qt/researcher/researcherwizardinvestorpage.cpp +++ b/src/qt/researcher/researcherwizardinvestorpage.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardinvestorpage.h" #include "qt/researcher/researchermodel.h" #include "qt/researcher/researcherwizard.h" @@ -16,6 +17,8 @@ ResearcherWizardInvestorPage::ResearcherWizardInvestorPage(QWidget *parent) , ui(new Ui::ResearcherWizardInvestorPage) { ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->headerLabel, 11); } ResearcherWizardInvestorPage::~ResearcherWizardInvestorPage() diff --git a/src/qt/researcher/researcherwizardmodedetailpage.cpp b/src/qt/researcher/researcherwizardmodedetailpage.cpp index cf948754d4..4d78ebde29 100644 --- a/src/qt/researcher/researcherwizardmodedetailpage.cpp +++ b/src/qt/researcher/researcherwizardmodedetailpage.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardmodedetailpage.h" #include "qt/researcher/researchermodel.h" #include "qt/researcher/researcherwizard.h" @@ -17,6 +18,8 @@ ResearcherWizardModeDetailPage::ResearcherWizardModeDetailPage(QWidget *parent) , m_researcher_model(nullptr) { ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->titleLabel, 16); } ResearcherWizardModeDetailPage::~ResearcherWizardModeDetailPage() diff --git a/src/qt/researcher/researcherwizardmodepage.cpp b/src/qt/researcher/researcherwizardmodepage.cpp index 01e1372001..2d2b1c1481 100644 --- a/src/qt/researcher/researcherwizardmodepage.cpp +++ b/src/qt/researcher/researcherwizardmodepage.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardmodepage.h" #include "qt/researcher/researchermodel.h" #include "qt/researcher/researcherwizard.h" @@ -17,6 +18,8 @@ ResearcherWizardModePage::ResearcherWizardModePage(QWidget *parent) , m_researcher_model(nullptr) { ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->titleLabel, 16); } ResearcherWizardModePage::~ResearcherWizardModePage() diff --git a/src/qt/researcher/researcherwizardpoolpage.cpp b/src/qt/researcher/researcherwizardpoolpage.cpp index 5ddd376568..e5c199781b 100644 --- a/src/qt/researcher/researcherwizardpoolpage.cpp +++ b/src/qt/researcher/researcherwizardpoolpage.cpp @@ -4,6 +4,7 @@ #include "base58.h" #include "key.h" +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardpoolpage.h" #include "qt/guiutil.h" #include "qt/researcher/researchermodel.h" @@ -28,6 +29,8 @@ ResearcherWizardPoolPage::ResearcherWizardPoolPage(QWidget *parent) ui->addressLabel->setFont(GUIUtil::bitcoinAddressFont()); ui->copyToClipboardButton->setVisible(false); + GRC::ScaleFontPointSize(ui->headerLabel, 11); + #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac ui->newAddressButton->setIcon(QIcon()); #endif diff --git a/src/qt/researcher/researcherwizardpoolsummarypage.cpp b/src/qt/researcher/researcherwizardpoolsummarypage.cpp index 0dadc02d7a..68f5f940fa 100644 --- a/src/qt/researcher/researcherwizardpoolsummarypage.cpp +++ b/src/qt/researcher/researcherwizardpoolsummarypage.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardpoolsummarypage.h" #include "qt/researcher/projecttablemodel.h" #include "qt/researcher/researchermodel.h" @@ -21,6 +22,8 @@ ResearcherWizardPoolSummaryPage::ResearcherWizardPoolSummaryPage(QWidget *parent { ui->setupUi(this); ui->projectTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + + GRC::ScaleFontPointSize(ui->headerLabel, 11); } ResearcherWizardPoolSummaryPage::~ResearcherWizardPoolSummaryPage() diff --git a/src/qt/researcher/researcherwizardsummarypage.cpp b/src/qt/researcher/researcherwizardsummarypage.cpp index a1d43952ab..31b000878d 100644 --- a/src/qt/researcher/researcherwizardsummarypage.cpp +++ b/src/qt/researcher/researcherwizardsummarypage.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "qt/bitcoinunits.h" +#include "qt/decoration.h" #include "qt/forms/ui_researcherwizardsummarypage.h" #include "qt/researcher/projecttablemodel.h" #include "qt/researcher/researchermodel.h" @@ -102,6 +103,9 @@ void ResearcherWizardSummaryPage::refreshSummary() ui->rainAddressLabel->setText(m_researcher_model->formatBeaconAddress()); ui->renewBeaconButton->setEnabled(m_researcher_model->hasRenewableBeacon()); + GRC::ScaleFontPointSize(ui->cpidLabel, 12); + GRC::ScaleFontPointSize(ui->rainAddressLabel, 8); + refreshOverallStatus(); } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 618c870de7..fda41eb75b 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -4,6 +4,7 @@ #ifndef Q_MOC_RUN #include "clientmodel.h" #include "qt/bantablemodel.h" +#include "qt/decoration.h" #include "rpc/server.h" #include "rpc/client.h" #include "rpc/protocol.h" @@ -220,6 +221,8 @@ RPCConsole::RPCConsole(QWidget *parent) : { ui->setupUi(this); + GRC::ScaleFontPointSize(ui->banHeading, 12); + #ifndef Q_OS_MAC ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export")); ui->showCLOptionsButton->setIcon(QIcon(":/icons/options")); From ebac5937d31f32c837ba32dda04628ec04d1972c Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 05:20:39 -0500 Subject: [PATCH 045/280] Fix macOS GUI sidebar icon scaling Because macOS uses a different base DPI than Windows or Linux, the sidebar icons appeared quite small. This applies OS-specific scale to the icons to normalize the appearance with other platforms. --- src/qt/bitcoingui.cpp | 5 +++-- src/qt/decoration.cpp | 21 +++++++++++++++++++++ src/qt/decoration.h | 17 +++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1d571fbb3f..873f757296 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -9,6 +9,7 @@ #include #include +#include "qt/decoration.h" #include "bitcoingui.h" #include "transactiontablemodel.h" #include "addressbookpage.h" @@ -441,7 +442,7 @@ void BitcoinGUI::createActions() void BitcoinGUI::setIcons() { const QToolBar* toolbar = findChild(); - const int toolbar_icon_size = 16 * logicalDpiX() / 96; + const int toolbar_icon_size = GRC::ScalePx(this, 16); ToolbarButtonIconFilter::apply( this, @@ -594,7 +595,7 @@ void BitcoinGUI::createToolBars() toolbar->setContextMenuPolicy(Qt::PreventContextMenu); // Setting a taller height than the rendered icon provides additional // padding between the icon and the button text: - toolbar->setIconSize(QSize(16 * logicalDpiX() / 96, 24 * logicalDpiX() / 96)); + toolbar->setIconSize(GRC::ScaleSize(this, 16, 24)); toolbar->addWidget(logoLabel); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); diff --git a/src/qt/decoration.cpp b/src/qt/decoration.cpp index 1e43b39b9c..03755c4001 100644 --- a/src/qt/decoration.cpp +++ b/src/qt/decoration.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include #include using namespace GRC; @@ -54,3 +56,22 @@ void GRC::ScaleFontPointSizeF(QWidget* widget, double point_size) font.setPointSizeF(point_size * REFERENCE_DPI / OS_BASE_DPI); widget->setFont(font); } + +int GRC::ScalePx(QPaintDevice* painter, int px) +{ + if (!painter) { + return px; + } + + return painter->logicalDpiX() * px / OS_BASE_DPI; +} + +QSize GRC::ScaleSize(QPaintDevice* painter, int width, int height) +{ + return QSize(ScalePx(painter, width), ScalePx(painter, height)); +} + +QSize GRC::ScaleSize(QPaintDevice* painter, int size) +{ + return ScaleSize(painter, size, size); +} diff --git a/src/qt/decoration.h b/src/qt/decoration.h index 3b5902f65c..c5f12c0904 100644 --- a/src/qt/decoration.h +++ b/src/qt/decoration.h @@ -7,6 +7,8 @@ #include QT_BEGIN_NAMESPACE +class QPaintDevice; +class QSize; class QWidget; QT_END_NAMESPACE @@ -26,4 +28,19 @@ void ScaleFontPointSize(QWidget* widget, int point_size); //! than other operating systems. //! void ScaleFontPointSizeF(QWidget* widget, double point_size); + +//! +//! \brief Scale a pixel value according to the OS base DPI and any DPI scaling. +//! +int ScalePx(QPaintDevice* painter, int px); + +//! +//! \brief Scale pixel values according to the OS base DPI and any DPI scaling. +//! +QSize ScaleSize(QPaintDevice* painter, int width, int height); + +//! +//! \brief Scale pixel values according to the OS base DPI and any DPI scaling. +//! +QSize ScaleSize(QPaintDevice* painter, int size); } // namespace GRC From 630850b3dbd40b9cd20a85c270b9c600504fc643 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 05:54:09 -0500 Subject: [PATCH 046/280] Fix macOS GUI status bar icon scaling Because macOS uses a different base DPI than Windows or Linux, the status bar icons appeared quite small. This applies an OS-specific scale to the icons to normalize appearance with other platforms. --- src/qt/bitcoingui.cpp | 38 ++++++++++++++++++-------------------- src/qt/bitcoingui.h | 2 -- src/qt/decoration.cpp | 29 +++++++++++++++++++++++++++++ src/qt/decoration.h | 27 +++++++++++++++++++++++++++ src/qt/guiconstants.h | 3 --- 5 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 873f757296..773ea74ce6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -625,7 +625,6 @@ void BitcoinGUI::createToolBars() } frameBlocks->setContentsMargins(0,0,0,0); - frameBlocks->setMinimumHeight(STATUSBAR_ICONSIZE); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); @@ -657,7 +656,7 @@ void BitcoinGUI::createToolBars() timerStakingIcon->start(MODEL_UPDATE_DELAY); // Instead of calling updateStakingIcon here, simply set the icon to staking off. // This is to prevent problems since this GUI code can initialize before the core. - labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_no_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_no_" + sSheet)); labelStakingIcon->setToolTip(tr("Not staking: Miner is not initialized.")); } @@ -888,7 +887,7 @@ void BitcoinGUI::setNumConnections(int n) } icon.append("_").append(sSheet); - labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelConnectionsIcon->setPixmap(GRC::ScaleStatusIcon(this, icon)); if (n == 0) { @@ -908,7 +907,7 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) // return if we have no connection to the network if (!clientModel || clientModel->getNumConnections() == 0) { - labelBlocksIcon->setPixmap(QIcon(":/icons/status_sync_stalled_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_sync_stalled_" + sSheet)); labelBlocksIcon->setToolTip(tr("Sync: no connections.")); return; } @@ -946,13 +945,13 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) if(secs < 90*60 && count >= nTotalBlocks) { tooltip = tr("Up to date") + QString(".
") + tooltip; - labelBlocksIcon->setPixmap(QIcon(":/icons/status_sync_done_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_sync_done_" + sSheet)); overviewPage->showOutOfSyncWarning(false); } else { - labelBlocksIcon->setPixmap(QIcon(":/icons/status_sync_syncing_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_sync_syncing_" + sSheet)); tooltip = tr("Catching up...") + QString("
") + tooltip; overviewPage->showOutOfSyncWarning(true); @@ -1331,7 +1330,7 @@ void BitcoinGUI::setEncryptionStatus(int status) switch(status) { case WalletModel::Unencrypted: - labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_none_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_encryption_none_" + sSheet)); labelEncryptionIcon->setToolTip(tr("Wallet is not encrypted!")); encryptWalletAction->setChecked(false); changePassphraseAction->setEnabled(false); @@ -1341,9 +1340,9 @@ void BitcoinGUI::setEncryptionStatus(int status) break; case WalletModel::Unlocked: if (fWalletUnlockStakingOnly) { - labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_unlocked_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_encryption_unlocked_" + sSheet)); } else { - labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_none_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_encryption_none_" + sSheet)); } labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently %1 ").arg(fWalletUnlockStakingOnly ? tr("unlocked for staking only") : tr("fully unlocked"))); encryptWalletAction->setChecked(true); @@ -1353,7 +1352,7 @@ void BitcoinGUI::setEncryptionStatus(int status) encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::Locked: - labelEncryptionIcon->setPixmap(QIcon(":/icons/status_encryption_locked_" + sSheet).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_encryption_locked_" + sSheet)); labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -1538,7 +1537,7 @@ void BitcoinGUI::updateStakingIcon() if (globalStatus.staking) { - labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_yes_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_yes_" + sSheet)); labelStakingIcon->setToolTip(tr("Staking.
Your weight is %1
Network weight is %2
Estimated staking frequency is %3.") .arg(QString::number(globalStatus.coinWeight, 'f', 0)) .arg(QString::number(globalStatus.netWeight, 'f', 0)) @@ -1555,7 +1554,7 @@ void BitcoinGUI::updateStakingIcon() } else if (!globalStatus.staking && !globalStatus.able_to_stake) { - labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_problem_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_problem_" + sSheet)); //Part of this string won't be translated :( labelStakingIcon->setToolTip(tr("Unable to stake: %1") .arg(QString(globalStatus.ReasonNotStaking.c_str()))); @@ -1571,7 +1570,7 @@ void BitcoinGUI::updateStakingIcon() } else { - labelStakingIcon->setPixmap(QIcon(":/icons/status_staking_no_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_no_" + sSheet)); //Part of this string won't be translated :( labelStakingIcon->setToolTip(tr("Not staking currently: %1, Estimated staking frequency is %2.") .arg(QString(globalStatus.ReasonNotStaking.c_str())) @@ -1646,23 +1645,23 @@ void BitcoinGUI::updateScraperIcon(int scraperEventtype, int status) if (scraperEventtype == (int)scrapereventtypes::OutOfSync && status == CT_UPDATING) { - labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_waiting_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_scraper_waiting_" + sSheet)); labelScraperIcon->setToolTip(tr("Scraper: waiting on wallet to sync.")); } else if (scraperEventtype == (int)scrapereventtypes::Sleep && status == CT_NEW) { - labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_inactive_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_scraper_inactive_" + sSheet)); labelScraperIcon->setToolTip(tr("Scraper: superblock not needed - inactive.")); } else if (scraperEventtype == (int)scrapereventtypes::Stats && (status == CT_NEW || status == CT_UPDATED || status == CT_UPDATING)) { - labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_waiting_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_scraper_waiting_" + sSheet)); labelScraperIcon->setToolTip(tr("Scraper: downloading and processing stats.")); } else if ((scraperEventtype == (int)scrapereventtypes::Convergence || scraperEventtype == (int)scrapereventtypes::SBContract) && (status == CT_NEW || status == CT_UPDATED) && nConvergenceTime) { - labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_ok_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_scraper_ok_" + sSheet)); if (bDisplayScrapers) { @@ -1688,7 +1687,7 @@ void BitcoinGUI::updateScraperIcon(int scraperEventtype, int status) else if ((scraperEventtype == (int)scrapereventtypes::Convergence || scraperEventtype == (int)scrapereventtypes::SBContract) && status == CT_DELETED) { - labelScraperIcon->setPixmap(QIcon(":/icons/status_scraper_no_convergence_" + sSheet).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelScraperIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_scraper_no_convergence_" + sSheet)); labelScraperIcon->setToolTip(tr("Scraper: No convergence able to be achieved. Will retry in a few minutes.")); } @@ -1706,8 +1705,7 @@ void BitcoinGUI::updateBeaconIcon() } labelBeaconIcon->show(); - labelBeaconIcon->setPixmap(researcherModel->getBeaconStatusIcon() - .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBeaconIcon->setPixmap(GRC::ScaleStatusIcon(this, researcherModel->getBeaconStatusIcon())); labelBeaconIcon->setToolTip(tr( "CPID: %1\n" diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index f9a32b5110..cb181eb2ab 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -144,8 +144,6 @@ class BitcoinGUI : public QMainWindow // name extension to change icons according to stylesheet QString sSheet; - int STATUSBAR_ICONSIZE = UNSCALED_STATUSBAR_ICONSIZE * logicalDpiX() / 96; - /** Create the main UI actions. */ void createActions(); /** Create the menu bar and sub-menus. */ diff --git a/src/qt/decoration.cpp b/src/qt/decoration.cpp index 03755c4001..2c322f723b 100644 --- a/src/qt/decoration.cpp +++ b/src/qt/decoration.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include #include @@ -29,6 +31,11 @@ constexpr int OS_BASE_DPI = 72; #else constexpr int OS_BASE_DPI = 96; #endif + +//! +//! \brief The base width and height in pixels for status bar icons. +//! +constexpr int STATUSBAR_ICONSIZE = 16; } // Anonymous namespace // ----------------------------------------------------------------------------- @@ -75,3 +82,25 @@ QSize GRC::ScaleSize(QPaintDevice* painter, int size) { return ScaleSize(painter, size, size); } + +QPixmap GRC::ScaleIcon(QPaintDevice* painter, const QIcon& icon, int size) +{ + const int scaled_size = ScalePx(painter, size); + + return icon.pixmap(scaled_size, scaled_size); +} + +QPixmap GRC::ScaleIcon(QPaintDevice* painter, const QString& icon_path, int size) +{ + return ScaleIcon(painter, QIcon(icon_path), size); +} + +QPixmap GRC::ScaleStatusIcon(QPaintDevice* painter, const QIcon& icon) +{ + return ScaleIcon(painter, icon, STATUSBAR_ICONSIZE); +} + +QPixmap GRC::ScaleStatusIcon(QPaintDevice* painter, const QString& icon_path) +{ + return ScaleIcon(painter, icon_path, STATUSBAR_ICONSIZE); +} diff --git a/src/qt/decoration.h b/src/qt/decoration.h index c5f12c0904..380e9b12c4 100644 --- a/src/qt/decoration.h +++ b/src/qt/decoration.h @@ -7,8 +7,11 @@ #include QT_BEGIN_NAMESPACE +class QIcon; class QPaintDevice; +class QPixmap; class QSize; +class QString; class QWidget; QT_END_NAMESPACE @@ -43,4 +46,28 @@ QSize ScaleSize(QPaintDevice* painter, int width, int height); //! \brief Scale pixel values according to the OS base DPI and any DPI scaling. //! QSize ScaleSize(QPaintDevice* painter, int size); + +//! +//! \brief Create an image for an icon according to the OS base DPI and any +//! DPI scaling. +//! +QPixmap ScaleIcon(QPaintDevice* painter, const QIcon& icon, int size); + +//! +//! \brief Create an image for an icon according to the OS base DPI and any +//! DPI scaling. +//! +QPixmap ScaleIcon(QPaintDevice* painter, const QString& icon_path, int size); + +//! +//! \brief Create an image for a status bar icon according to the OS base DPI +//! and any DPI scaling. +//! +QPixmap ScaleStatusIcon(QPaintDevice* painter, const QIcon& icon); + +//! +//! \brief Create an image for a status bar icon according to the OS base DPI +//! and any DPI scaling. +//! +QPixmap ScaleStatusIcon(QPaintDevice* painter, const QString& icon_path); } // namespace GRC diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 713a1cd278..8d63f00559 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -9,9 +9,6 @@ static const int MODEL_UPDATE_DELAY = 4000; /* AskPassphraseDialog -- Maximum passphrase length */ static const int MAX_PASSPHRASE_SIZE = 1024; -/* BitcoinGUI -- Size of icons in status bar */ -static const int UNSCALED_STATUSBAR_ICONSIZE = 16; - /* Invalid field background style */ #define STYLE_INVALID "background:#FF8080" From 6f0c870d5eb5e853fda3fec40be8a9496eab2d5c Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 14:14:02 -0500 Subject: [PATCH 047/280] Switch GUI overview page form layouts to grids This improves the rendering of the tables on the overview page on macOS. Qt draws macOS form layouts with right-aligned labels and moves the form to the center of its parent which gives the overview page an awkward and broken appearance. Using grid layouts avoids the custom styles and shows the information as on the other platforms. --- src/qt/forms/overviewpage.ui | 181 ++++++++++++++++---- src/qt/overviewpage.cpp | 6 +- src/qt/res/stylesheets/dark_stylesheet.qss | 4 + src/qt/res/stylesheets/light_stylesheet.qss | 4 + 4 files changed, 158 insertions(+), 37 deletions(-) diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 319cf0007b..8176986061 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -104,14 +104,8 @@
- - - QFormLayout::AllNonFixedFieldsGrow - - - 12 - - + + 12 @@ -119,10 +113,19 @@ Available: + + true +
+ + + 0 + 0 + + IBeamCursor @@ -142,10 +145,19 @@ Immature Stake: + + true + + + + 0 + 0 + + IBeamCursor @@ -165,10 +177,19 @@ Unconfirmed: + + true + + + + 0 + 0 + + IBeamCursor @@ -188,10 +209,19 @@ Immature: + + true + + + + 0 + 0 + + Total mined coins that have not yet matured. @@ -208,10 +238,19 @@ Total: + + true + + + + 0 + 0 + + Your current total balance @@ -272,14 +311,8 @@ - - - QFormLayout::AllNonFixedFieldsGrow - - - 12 - - + + 12 @@ -287,10 +320,19 @@ Blocks: + + true + + + + 0 + 0 + + @@ -304,10 +346,19 @@ Difficulty: + + true + + + + 0 + 0 + + @@ -321,10 +372,19 @@ Net Weight: + + true + + + + 0 + 0 + + @@ -338,10 +398,19 @@ Coin Weight: + + true + + + + 0 + 0 + + @@ -399,14 +468,8 @@ - - - QFormLayout::AllNonFixedFieldsGrow - - - 12 - - + + 12 @@ -414,10 +477,19 @@ Status: + + true + + + + 0 + 0 + + @@ -431,10 +503,19 @@ CPID: + + true + + + + 0 + 0 + + @@ -448,10 +529,19 @@ Magnitude: + + true + + + + 0 + 0 + + @@ -465,10 +555,19 @@ Pending Reward: + + true + + + + 0 + 0 + + @@ -495,6 +594,12 @@ + + + 0 + 0 + + 6 @@ -535,7 +640,7 @@ - :/icons/warning + :/icons/warning true @@ -720,14 +825,8 @@ - - - QFormLayout::AllNonFixedFieldsGrow - - - 12 - - + + 12 @@ -739,6 +838,12 @@ + + + 0 + 0 + + @@ -756,6 +861,12 @@ + + + 0 + 0 + + @@ -775,6 +886,8 @@
clicklabel.h
- + + + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 67632952c1..0f01849c6b 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -127,9 +127,9 @@ OverviewPage::OverviewPage(QWidget *parent) : // Override .ui default spacing to deal with various dpi displays. int verticalSpacing = 7 * this->logicalDpiY() / 96; ui->verticalLayout_10->setMargin(verticalSpacing); - ui->formLayout->setVerticalSpacing(verticalSpacing); - ui->formLayout_2->setVerticalSpacing(verticalSpacing); - ui->researcherFormLayout->setVerticalSpacing(verticalSpacing); + ui->walletGridLayout->setVerticalSpacing(verticalSpacing); + ui->stakingGridLayout->setVerticalSpacing(verticalSpacing); + ui->researcherGridLayout->setVerticalSpacing(verticalSpacing); QRect verticalSpacerSpacing(0, 0, 20, 20 * this->logicalDpiY() / 96); ui->verticalSpacer->setGeometry(verticalSpacerSpacing); diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index c7cb825c2f..38488a6800 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -512,6 +512,10 @@ QStatusBar QToolTip { color: rgb(250, 250, 250); } +#OverviewPage QLabel[isRowHeader=true] { + min-width: 6.5em; +} + #availableLabel, #immatureTextLabel, #totalBalanceLabel { diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index b85a956da6..c5423e67c5 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -521,6 +521,10 @@ QStatusBar .QFrame QLabel { color: rgb(43, 52, 69); } +#OverviewPage QLabel[isRowHeader=true] { + min-width: 6.5em; +} + #availableLabel, #immatureTextLabel, #totalBalanceLabel { From 7b7e6585387ea5407415c8a90dad8dd29cdd9ee3 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 17:02:21 -0500 Subject: [PATCH 048/280] Replace Inter variable font with static variants Since macOS doesn't handle Inter's variable format, this replaces the font with the static, non-hinted variants for consistency. --- src/Makefile.qt.include | 4 +++- src/qt/bitcoin.qrc | 4 +++- src/qt/bitcoingui.cpp | 4 +++- src/qt/res/fonts/Inter-Bold.otf | Bin 0 -> 270856 bytes src/qt/res/fonts/Inter-Medium.otf | Bin 0 -> 269160 bytes src/qt/res/fonts/Inter-Regular.otf | Bin 0 -> 258288 bytes src/qt/res/fonts/Inter-VariableFont.ttf | Bin 805068 -> 0 bytes 7 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/qt/res/fonts/Inter-Bold.otf create mode 100644 src/qt/res/fonts/Inter-Medium.otf create mode 100644 src/qt/res/fonts/Inter-Regular.otf delete mode 100644 src/qt/res/fonts/Inter-VariableFont.ttf diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index bed56b3523..024b1099cc 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -437,7 +437,9 @@ RES_IMAGES = \ RES_FONTS = \ qt/res/fonts/Inconsolata-Regular.ttf \ - qt/res/fonts/Inter-VariableFont.ttf + qt/res/fonts/Inter-Bold.otf \ + qt/res/fonts/Inter-Medium.otf \ + qt/res/fonts/Inter-Regular.otf RES_STYLESHEETS = \ qt/res/stylesheets/light_stylesheet.qss \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index ce952cc27b..930add0204 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -181,7 +181,9 @@ res/fonts/Inconsolata-Regular.ttf - res/fonts/Inter-VariableFont.ttf + res/fonts/Inter-Bold.otf + res/fonts/Inter-Medium.otf + res/fonts/Inter-Regular.otf diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 773ea74ce6..cb4b2d54ce 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -110,7 +110,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): * 0.4,QDesktopWidget().availableGeometry(this))); } - QFontDatabase::addApplicationFont(":/fonts/inter-variable"); + QFontDatabase::addApplicationFont(":/fonts/inter-bold"); + QFontDatabase::addApplicationFont(":/fonts/inter-medium"); + QFontDatabase::addApplicationFont(":/fonts/inter-regular"); QFontDatabase::addApplicationFont(":/fonts/inconsolata-regular"); // This slightly enlarges the application's base font size on Windows and diff --git a/src/qt/res/fonts/Inter-Bold.otf b/src/qt/res/fonts/Inter-Bold.otf new file mode 100644 index 0000000000000000000000000000000000000000..034970bd1457997232d4d319dc94a93f76f8450f GIT binary patch literal 270856 zcmbTfXS@`}*0)`uL-o!%?+H6AQB+VsC4&l*QIQ-Z=bV$0K|nx^1eKsj2FV~GC`u4e z5D+#Q6G=n`gy+BZ8c(>N`+YvVp5MjQyQjOlYSpTq>FPN>bsIIRDWF+c45U&S>#J}2lCx3F2iY=on{w~tPu|kNG6Th1g^2%Fl?hei99Pt#k zb%ZK<8N~?^w)pQCgvcCuYwgX~GCCKP6nc^5-4LRl@P5Oh}v z?1_ME5Lyn$a_`V`#D6SD%7B~^S|24IQLA_v;Y(5%L(5WRsJo$MCCclR(6Saabf|pv zcA5^=Q`#a<|5J`5V)U-i_Ab^R2`w8@T>n$PCvvQ~(E2b@#wr_H4i_b@j-llUkzvga zEk}w1>!0$YgwHxIs)`<>rx+x9i%z10=qUOk?n^)!QM!l#BGgf|$C{@_cf8sjTL#em zy|G_s{MEK-kIJGS+!5<~i$4FcWfiRHD%$&0Hcu15NkBZ0ezcyI&uy_Q0 z_1J&x5%||0O^S}*2Q}@1x&*LoN!0QoY+V6$?}QqZ_XGTN>k70nr*Iw)-Eg z?1Z+^D*=?;8*Yc=cY}M2E?CzCnHDH&Z#QgL@?T30utgti*RN>r%0=Z;)cW9mRBwuL zU&Oo*+Fc6&qgS|uZYaN0QM~!cHHx;U9%)!~w7z0M(b1_lc#E!ZTkO>xWwbAK_HTtqE zjuk+Ssb4x`iQ-s8R2KD%miwahfq%t&G#d?Z2wfx^}{~ZIW-9Ov>Z?&sgR6dOl z%CL4tb^9j^`iREpRm#5qu0f~&Iu_N6`u}D0GvyGCXNq{wq9b?2YwhvBs(6Jmgz~C2 zwhM@w|D%+iMg7*WsO=rFMSGOfr>G<c&a}H?-`aVv z#J&6X>GMHA%#+W^&($?8bWqpVt#ljRR}a>&Aj(tq`}&$SFBIjPh;m&-xe21&CKTmu znRop${uF<}U*NCgujQ{7igHW;NdI{M3jaF)ZvS`wAN)7`w*n#%6DSqv8R#1r92gmx z7?>8A7dR1$vOtvWq9`W?eTedd{}tt?!J)xv!6m^J!PUWyp(vjUo)1OYMwDa!Bg*YU zQO^BWl=mabt#Z~O%JXtp<*v)!n7ci9SMJ__McMwpqC65&{vA<{`(L803L^@O7d~59 zudsPxYeczA;p>GT7OpAWT6nSWw==#o#m+o-=E*ZHuSi5W5Q;JaErk7N(Z4IOw5(`_ z`1?3pi2HBd+JE^~Auf*suYl2@Vc_Ye=LcVi zz1;mgGU0L+yc&O@HvWD5{PFWYoIiIV`$GB!eL$>-L9kIyYQ_vX2J=gOQbcJ8Nh zKb|{+b$ieCIM?}H2h?QZ*&S!=pDlOh@qdm>K4VVzIg?O$uJCN(!oo4g$g-z5oL+Z& z!s*ec7o5Q-`xEC*>^*V$`;p%cJhAU+cH5Gz544(}cP20^@I&yX5Lx)_ob^|>m$M@0 zst~!IbGzh@r&2L8tUMnrMXwHD5q4cj*-iy?Ppb_+&#rN<;RJ$qxV7B|ZZr60cd{`a zUZelQ(m+OtqB4rsVh#NlQIXC6Mzp}1IpBPxh)j#bnJcmeXcRd*@+?gJ6nP^mEUMPO z_ll}U%TZO>|6Y%EPlDk^uTK8Y^}I~4@T>p*S`^NSp)ET9vxF_%M74{0IjU<^_o%-A zUi-f-Md8}w|NFlvv?mI&;{T$a3At6&s?gG|sQsZAg)_}AV|TS{TUVWz?1$_wc18OM zd#U}pJ=ktw53!%KqwN?w){eIm>_j`+PO($%G<&!`%AR6Rx2M`=?P|C-sf@SDO;c}9EQ+_BvmtV*O@>}_<{iI#ru4I2~FLv6?Kh;yJ zhH9YNsaMrRHB&88JJna}u)3!H(5|kaE9>gIt?q}j*N1wEUZp?P`|T=rUAwCNrZe4s z#JXnPu-O7k3#IvFm-q+gT{e7?a3fGjo-HEcetS)QFnzD+lY|oNa=LN~&aKRJ_Vi&!`Tnqw1vEtL18iTB(*;dFoFcp(Aya_O!8Q>oC02 zO~P6DRlUi|*Xu>NiV+d=wun@*qPBWg)KRs?bE=N0tDX~0)C=xt)ljrkxJpo6#LKFy z=%BiZj;gzurlyGLYO0u_UK6jYY2tk~S1eKQiGAv0u^-n_2h?hDP^}f;sKxGdwL^xf z-7;G3Gf&G@bwZ}8lQLcX;LcIUWk4N~CDc7xQr(xO@aA1s-Ild zBXk`(Qa>k0>AG^Vepyb@9pqHqQNE@-$+z@S`L-VBex!%UkM&r&R8Ntg=&5pTlUwR@33`yQYgAte%JwQ(vFByLL5@-_B39v=R7HzU3fFK)#QFBb* z)CWbp3KLyaFIi6~%lbM+HqfcEp-z*>^%nKLx}yiG&E|V^99IJMRZSU`@9XyZ8*{>( z6xHP}^Me?qUY6l%tFET2%89zBEYurSihJ1oR#h>*)zhkxYHX6t`(~k4%X->+)_O*5 zQIkw@_cd#sdsMxx=BPK-GOL?ALw%=?siWpabwmB8uB*H1FZXR-)_uoxHG_36GfzLQ zU)0a*XZ17g`|bz2ok=w{OomCrr;`DCf_KWhpcm?o^_T8yeMooH1uju6jqkYacKb>~HMv&3u#R zerTVv3+;jSAoq^l+5O8*HWSSwrmTCw{l-1y?sqyly-fp?Zx)*ez4NA(dEDLR-gJL6 zQ_Ljqw23n#Ok1a$)5GcM^m95p?aX*H+7z=7+sEzG=27#x*<-#mUzpwIyt!a5noGtq z&pU&h0nSKgv@^u?cZNA*oT1JL=M`t5Gs-kF4V}U6IcK#R~DgJgYBQ-7()(OP|mu#SnQ*EZ0AXV=`G(lm+rh zeM+|1r{xe`C_j*A^ci_XpVjBYae2;qz^Y+2wt83-tpclxRn@9yRks>ijjXO#H&fFZ zVcMCOt&zI8HOd-ojkU&E+N?%cTdZx?c2mXLVePT@ zT3_pO);GG6s&5_AyR5@{t@WLC%sOG6v`$&4tskuO)&=XL8mUI9H%v+EN8M9xSD&db zt+UoSHQPMnmC>JDN8R^KH}@;+jQgrdaMyXixSx8zx@)|j+_m1%?rc-nU0_C9Mn7uh zSXbQFO?CGZUEZB*y1VmC4|l%l|Q$F7pcA z<=z>2$okfbw0^dtt?#WE>$nxG7Ft2;7k9Y(iy7!{^sZZuwbLDIeIY)w4v3}JL3gBk zMdn*ysjK24l_tulbWut9#dtN^$}%6gi_M4bChvy3!Mkc@TffTkrmC!9D$8E_G1wo57gSU6qIyX*Rn5dp zs=4T@dW&wVkLa#&J+1nQo~pl?q{fTc>UHsY$8Q-^dJgN@l9l(x(cgU!9RT>ZZ(9zso#zOFpEPETgr2SX=TDZOcYFT{hMk zvWd==&ugC?pr4cj^;7Z{-9Wyo8_Kb|ksPNR%jvp{oT0nQ*L62JQ+JoMbPxHi9wFb; zBjsE@O3u@x<$C?5+@Rl*8}-|AlYU2T*6+&QdXe0t7t6i+6Zw^1CcoAn$$fgc+^<*2 zgL<|6Mz4`4^j3LYeOns=w=#_e#ic{m%SZ|=5;|&stDjc)5Q6gC>k)kY- zs%(*_9FeZH$dGq1`~0`?$$P>t?_=iouIQx(h~8?T=%WUSzG|@Or-q3BYN!~XhKYe{ zxcF9W5Z|ed@&)aeFY177s)O<+oh6&;Y}s7r$QC+Rw$ypDmCl!~b%AW7ACPVJgK~?0 zPj1z726ht@)Ck-n%e>C5^@{geJ#|Du1j%6KEK z2Hq&Eg*Vz7q#Dz=I3;xn;B?6j6xA6Xw;ORZ0=W!7?Qg}$n<>FfH2{!QQ1zw298C9AS1CW>SB zrj)4Qjq}EP6TFGuB&&nf(duM%wz`Nh;uWz$Y!th^$=+0Nnl;!OVh#0XSi`L0;+!}y zE{KcX>)tHy4QsabhBe1~(;8#FV!dg-B`?b#1Y zf9s?>8BV5??4;Pg*?&0F3G-Ilx9z{}d-i=tILfgd$7I^dw(JP&Z`-wn^_LxH-L;Ks zYu&Q$*tYF>YpmP0wC-W%N_%U)b=IHOAJ$FpQ`@tCx5MoyZ@ss{wOz+)<+OC>*(>D) zIo@7vud!EoXPw5*3rOns?1C^SXJZn=M6=&nh*M6Xy~jD`EOtI|);S+LE1dP}6SYQdQrpxn zT|?K_P4o-)CVR7e#lC9aw12m6*?-!9Iok1@a3{h^b<)f!ZX*UXp?AS}#H;5<--^AeoL*bl+^ zjNJt;01u!{nqzp7v6sTd82fd&IAafnOE7i=xFll_flD#=b8u;ZI@;0j!{AXY$HSN{ z!dXGs$#8kbrn!d-j6DjbdH|bdik@KX>F|?`Jr#b6v8lXD46fq^uHzY8{@{)gL+h%B zcp9edh(>^BorsnIa}Gsd4hyr%4Ap^VQHc3~_9s3EG%Eq52S3ZmD46;H$P~B^BdLEV zEoT$uT#u3c;rak^mBZkMjGP2hU4eWPZp_FJ;UZ z4^Xu3Wk$UUcVN^+xFe%x!krkk2=2_Nop2XMeFb-A)M2U&^Ug=uU6oBDkolGh_uwS9r#||Dk+kh8MjwYy zGrBii$f!v83?u)7&oWBF=NOd?Q=b7vZNI>%c=#ftGT=*$dIr7>e!})0;GY@Q9{vSf z!FtM5qKd+;qrQ+@tm?Ab8w19TWn?YVD8k3h6LK z?-MST0p?DHM7;@m-=OyuLe+**Z=&e^gN_X_$0{Tpk77_4regriy9#BoU)KP44oHc zE`xbyA(2l6o#Q0>m{9M*1&r7SKfquuP~Vng6xI1L#ykx_ z9)kLzJflv)6+%#-RAkgi80V}asDGYh6wVp)DaM@xS7Ow0xN->UlPZj&^HkLk6pw0* z!g!+dR}mDO8jQl&k~Kq6oN6%|T_c|fL2Z4OQMcjRA!yq=jHWm|7lPVem(j>|SuX^Y zSD(?baDx#2;D(H*m^BJ90&dJ`Iu4B`VkCH;(R552SHvjrBBSZ}O+!qEUt;vjaI+9o z;O2~`@zNs1RJbLhJHo9(yau;sbSJn?h_~Rjj2;Tp7$)8ZG;V<&2Gg;D`w>jX19}Kd z#~>)rsa&AP!kt4bg}X4C>fANNCvZ1LQ=PkqSO)iCG}XChh~;oEMo)u#hgbpkVKn7` z-wSI9H334>!(*9!@RT+MTajDL)GPpY=B*hdIeZC)uWl(heFdoYQ_l|^| zz|iM$Igz2y`*aOJxK#FJMp50UKLGAF2}ykb=<}tdxC3_)OtA(o9h2e<1eHC5!Tl*A zUuOjEHB*AYo>T8(x0gB>Dxd==>_#MVj{=OUH zQTRPZ{SD6zK{+xnL^XImqr>3^j7w#_&zLUo2O%g9A2Rw0cp>AigefirwPP`(d%;T> zm->Om2{7sK$Bga|Q@;{a7a9Y=tcNM@398rf5OmBHjHdY0v4Q>*Ud6cd`sxth!D|@3 z0bU#8cX%D655S)?E^KV&eX`(9j2#DWW^C%CEsWUOf-{C^}XMm-6DP5KqCBD*$&bh1|!8 zD0n}kkgk&24Cu44Jjf`D$2TFKh3Pe*sl7Dj35qw3bD(Fyl#9eFK(Pjza{p)u>W5>D zrg89n2`be4-D?F3VDhV(ePHdoDgQ}i%>rJUWj(^eFpce1?3YVVquh9gbQOOA>v>i0)3|}hUOs@&a*}I zfRV2R%}FTQpXdn;BhVJ=?;RTx9pa=7EbVBAvQ7xn*ha3rG>;V6ba%P6V`&`B`* zwdk5-G)!#(x*kmR2J~J;*I^nxE`=yRW<{sx-bSDc~GfT{$esjVd$`aGygF`C+4nxW5z>LEr` z49YNcK2Q%cdJFtW2%Iz2qal!|sw|@i!sSA2h93iuqwTbPc}7rODlmfTSusRS_z8xt zd(@K*z337l^FdETp3hB`6uA2jHnJ*W8^NF{s#E|gitjYny*rHJmO_giz&M1 zd>YHdR`3j?sk~n|{d`u#=5P^_9VW+D6%Xok53YF~54qIR@kENWlN5Y)e| zLQH~NGlp`i4dcEBw`Ht#a68653RB+@Zv%>H2QUY846zLE#8}#jz5qwgJH@Q zpecs*H!$U^JO>6nLj#>Q~@hfG0D0Av}fAAH!1_{UuEO4cyi6G)5nS zr!)F{cm`wq@av2T!ZR6D3ZBK7a`0@%JPg0Vxc6Yn72tdbzsZ=>@LP;U&j$%ScbEC&Ob0tJDB&8D|V^7-uN#G0q4$jB#Fp z!x;y;O>^jkGYXDmOd~joF%98p#u*I9Fzz`xmT`u|=x@Tk3&%6=?{EU+-i8wy_ZFPQ znDTHk<9-IGfKJbiO+Uhz9WZi~a4}Zsy_s+^hIBc`EQKFq%*XKKjG<#ue1UrguE3aO zFvSI!Meq}hcL{!yF+1U>7}F1~#27kGWyakOS78k0b5+JX2UlYZnhtP(hUT^Dy`S*1 z;K7WCaZGcdG(YN5j!+&0oBD19L-$uSjTzv)3{(CA2gjp%QbJIf;~1Mv`vWl)p1_C| zFy$W*)Q^;ZKvJ31_kiXAXwI~V3-C0?>JCq5L=c|A2#VS3U?w;LW-)@=G@GI8Y`Wef z#Bz8}h#%lL8F37Li=q2D`fY~3hoRqL=-!Bamywhs?}a!8&t+tLcwUIp@O*~mUiJGS z3gHhJnyaIG=LF4#(C4ipXn7G+GW96I)f4`Tu_nP^hoJKi zotuC)8Q#xW)K>=>FAqM*xD-!1?*M}~(`Q+lBiA$@zhx}Sh3^%ga-X|G%3`{u(Of*b+16;~o$`xRqg(*LPyAY;40A>MvjWM<0>x>x(-(ct(Qvb%# z=OcZSq5Bg0cYyPa_aIE|0fPGUHbeLN^c}`43EyR`SK+_Gee7Ebal~$x#6RdGONGdV zHRIyA7B(uv58DjQom&oYvF|0=FftPMKp02?;UVJS2*#!UrtJXT-?O3^7yWM`jzv%z zF^nh;$1+rRi^>9`CLGVW$Tce=1hpxV5ooWK6awwEa4sl<+DG*RvK*Ys&^<;gEd;eY zJp|fmWiWK_)XHSUCfLWwo-oBI3w^y4&IUPvVnHzl?j9I9S_H)-KLo{~02Bw*rV@bq zhw4J@2Q+tOJp>*G$Y<-35S!sg8M^MY%7)kiQ`;!U+W@r$u% z1)c^cz%u~j$-)@3YKQm%t^?|V^PnEU@zqGU0YlfFbiN}D#)8!-#E)=eMpNH40ncOK z?eGf>U58jNf~Mdsc!{CwTB{jjs9wz(uMFIRDLVJH#4>Rdv|`-%;MR=k2Df3{ui&;J z&cN*$mvXp0V<^X8X54jf2gdsa?#Q^G!krlJSGY6du7SHS-cN8>#$5|jJ%IN!+?{c$ zjXfAc@ue66cLCgsF%;+CA?O^?hoR4QR^JdgaK8{&;QoyJIy`_elnVnH_Y-&!L!a}k z!Hhc>9>N%^<50$(2M=Qm)pt1K&WA@ZhU!Ii1@0_(6k{llMl)suJccp5;8z$!b)-H3 z^yC+MKO+p4F%C>X+#ZG}GLpv7q!6FMlNqlpJS9XSJe8q)yw+I?Yhu95MegOJjo%Mc*eeeg2p#J?Z#C~{T2p_yC#ASGKhyc7K1m*2Vj5{8t zV*rx^FJ)X>N5=q$a()@(l9w|c#eW6k(z=z5cLrX?$V2ez5Z}U777+>7hWHs?7a|&_ zu}6FlD857tpxh#k1L{j67Hnc@j>*~_A_#8@@e53S2HfHBHpZp(+Zi(u{)};{+#QT} z9o`wjfvGPET1Wi{+_CWP5MRK17(p?nzlj5Y&VxWujQ55(2!F-6BjK+Z_X@m^k@@ic z5VS3o1!yk8qWyt*2>ymqX)whdh%)eDhVIW;M;K8Frv3nQPp0TS<2!6m`EZm`qv2yA zvS8{DU?>NVGwx#e1Y;;4Pckm`$qx)Y6{_g{<`iN@{db!2sQ!f^vSG>_;#Y8%k>%lY zjHwErXJiHV0%Iz}7a7?Lrg01C+Sj_w$Ug9ojOhf^7yz;_O!)^)XP9yV$M?#?)CYh* zQ(IJ5K%c>_YYct9wkSUUeIB={oq#@TTfZ^%+1$FxxO5)jJpc{ zi}9%Lw;B4ZY267yvAxSk>f66VP~YAI*w#aQ>{1NnT+~yH9VtX6;z-!2TV_qh#x|L? zz|&Zdc4gLKY~)>LUB*5H*JErPE3*M(BX2UBFg9{0^Lg+B$~ghQ#MsD%%r=aD2X4za z7TgZB$F;Ky<2*^&hE5k_Yhld%O3->+3Na~YE8JZLKm1XEVtiEy#%}x6rW9U1rzQ-AL z8ZOUBifaW%6~Yx6NijyQ6EuJAdyBOVm9j=r}T6$ev40Y&Ymz5$BjLHz+d>O;yWK;QB7Q4Rt8Kc8=J_aHcUcx9Fral8A4gQ!>4!o2R z=`iI1P#UJb2O_DjQkA#mC*~} zD~#L$UuEb%jPDvFcf!{hx+mki!AOe7Z;Ym6-DD)i=68nfTlj7xaPsfIgvlM?d<&Ukd9n zZvCY}IjqN6^FIcj!g};gf|MtcqVA>)8CKrM#KFX zn$PnOV7xK#K!)xK`{^~{y#iAl0nK6ghcMo&@KA>4g!~jcfM+uZKh;DV&pFbXR+^W znDznQn=s`Na);(b{ge}zuuSVO1B_Kk{)Lg$KfeOR(t8WO0d8V@%I90)4=f|T{y!Pn z4gQOfJ>c7nz5?H2G#&pgqp`pLZ^nBMzQ=g;gb1LifpA=d&xB(b^CTS0m`ZRQV>gE5 z8M_gj01|P{-ULo!JhVTM%oxNwkOES%O-DG5v3tVljClpl06uJkb_M*5LEHkUTp)<` z$hSZiW4{1rGxkex4r8dyT*gK}2J#piu?^%i<|!EYM>y#BKxyz0mYcz4z#}*o#%kbE zP!7xe;Kvvj@eDi;%47WmxB_D^z5*2)8)GN%1Y={n0L2lQzAzmdm_snd6u8KPKxM{7 zt^}$u?ssri#%>B%W1IrGI%89LH5i-9s0nJJZm4(QX~w2@Ji~Yk;Aa_oD_k4Y!S>Yl z=NRvOxGrPH!u0_9*82c{p0TMd)CVoF{$sc$<6w*gs1JaB7N$4?`yfpF48k(TL0~XI zpV-%6>ZjpY#yAL!VC-+Q~@MnCb+a zFnA{8t%heY_HB4JcoTKN_z1kkI0}9nyn}shcphUi;rWbB+fYn_ZNU^fU`N0dFJMub zA2PNJFJw@p2rOb~&M&Z-v8kSv>wxC@0v|E9fhpENwS|{5G>;hggrPaYz%sBLb)Y)0 zV7xW(N`~eX1FIOD;zO~(>oj*5Sj*TNUI#u!|I)hk49!ypHZU}I8Q92JH{nf;hkXK@ z8QX)mFf{iWpqvACIJ}Lqqu>+ZB#uSL`2n25xS;x~*}WyYi9P~HF!#|a{iLA1kZ z4Wr(KGattOgwqOk7zcS8M4bo+eHM&o401V`#5m~ZU@~JPWa4`N&rP74_0k?;`9U_-`f z05@fv#_&swLv3lsIP`jR#(5E@vVikEOtAn?6PRKI%p92F2+Ul#4P)Mg+cIVr+>SA? z!|fUK7W^_}FqVQH7=tky?8ulm;7*Kr8}7^)jNM=t#=Hl2Wy}n?8)Ig}-5G;%9PGiE zci^6k=>>-{2e90mG02}_AI3ZY_hn20+>bFBXTkoASppAWOguc0G1S&UjG;an%$P)Y z2xIocLjiKc+X_?rfrGIgq;>)aV<<>%0}jSD#@Pf@`w>@XB~0~0 zeI3gG6#(m;yYOnpxdX3ZoO>{p1)SS39RuY#zrhC?huVIKahAhJ80S;?6gZ9XDB<(q z0`|QRUt*k};1JHw_}ZId?9X7Ev3J7`L*M<$ie~Jca4ci*fa4e&?axYM?9X8zV{eBs zh6sBXT#K=hw^_(v!bVK9ke^xTEBiHEK4`}I6-Ips=L(#{IKRNTjB^dn1NqqI7L4N% z&hPL@#<>cQW1Q>oB*wV`qwfjlFZclC{3%3EHe)`4QO6vN2R92|2e6&%!iXW^28GC- z2Ns~5@8MO9gY9zBzl3ub#=eB}4UB#woNrgoFC!;ygk)hv4mu zgIvh{jB(Kaxri@OwElA}gQE55%Us|fFLL)VP9eOPv1#2`jB^a8dIRSue37wVgs(C# z`UfE|!Z`<{fASn`e+jP0IA`J3jB_4t&o~!g#ENjf6CyvJalVF=8D}5dgmI9^`N$2z zL0$4k0*q7Yi+t3F@KE=B^f%!whHo(r`Y!(u#zEZk(XWK_F^v8u9E`>M+l;dwmpo_( z;rs~40o2by{uj$&TuX@JLmB%nJc5xpP6>=L!b4k1rZ9E|Ar{VMJhW>OUMD>C>5`I+ zhgcU1#yJBk5RMgZz!9JrmQkO=;@}y4mI%PlGW7qkD6GvWoIeWdFbX+T_#C6Q!F3t6 z3$6#6p&ay4A>L~UT?1~xXezHIqtOqAtr-0Rj6N;wgzcNcof+K+?gB<){HDXNgV|Uf z4Sxt0Vwvi*2%vxLD=^N7g&$-2CcG4&9X9%=a2exhcsb*EF#4&G&Y9sb##iBLET_WM zW*Qeb-xO{I$S>ys_#)$Uhks)X^1qOd2RteR`9hd;Fmi@4)GqWTVJIF{CtzNJkuQXy z*dS*JZ$5m7@utAY3BsER|IK)>!S@&saVbRq6W(+o(A`CNb70AM@50Cd!kY^t7KDrP z&LIB?7i~RbGlrItvxG6Q3lKB+G91RZM_|N`z$W5M1Y=_1NXA6LAq>49&6xUd3}Zfn zX&+#k!x$rkLI0kKXAB)PfpO8!Gl`6u04Fi-H8`0usOy;&#!y|*uY`;9$(c09JprdP z?iM(MaZkcN#?X2{<6egYj9Ul?8AGwjVhpt@n=#Gc9L7+bav8H7&STsga6aSy02eSW z^5YD0jxb-t4>D#mTnv;$TgZ=rC$L-*eiF36G99-iXoF>Xy)9!OgWECgRUxh*Hwb$q zjJzNO;(P^pLC9ZVd+Lhlpe)=tLhFT}k7D1p2>2%~QZ55IbLy;KpxUICW| zPoq9nVEkqn5ca2Vmm-9{PRKv&6(O7@aDyU*vkY}YUFg4ibtHQ7|Nc+Xw``@9;ZJw( z+rD>+Dm}Wkqdz-l3K2d?(7%J}c5C>Mh@wA+D*9uX{xiY4TQfrcF+alc%@dE-_fMMi zk0ad7`}c3HxtD@1Gdkm+h^K7)JLgO^{d+NHbEc4|HpbB(mt(y@l%QWoixdeWU1W&| zL}^h@JSnP++M*%u`#k};#YA~ z+>r{uFc2e?rBCL{;__ixURK8M%G8xjWOMw|Ocy*Eevlj~$H}R3mV8IPkEivolPvM{9aX2)1@#M_bADS(ZFICw(wRC(7t>|*9L{kC3!-!WK$-!a&#KgY9+595~v&fp2LSM@FZH=a8kZpGp0)B!6W z&!m0Ss)%Q2KV#LmUcj?pU$(m8$+JVO(Rfz&G(3&RH5AgKRRd^!lc4v>XA5R)R>6~+Za<1dqqxW6MjdT;-bT`X= zz%A{TbDwmpa+0N)c5hc z)Rktv*@ow{@59scPv8mhKbmXi4|C76y$CPfOY?$wu6QZ0toMXh&3o2s;JxUz@;Z3k z@r>x9-WWVFm!4rd7f+V`#9QNS@^*M%dI!Cu^t5{K7wm1fAY+%@ku(4rN!e)lO9kw8BN!W_8Ps6r`eIE98*x|6_ zVQ0cFhg}W374~q9PI_G9t1g9*lS> z;<1RQB5Fj`iD(q@Qbe1GP7ysL21E>xcr{{j#Oo1nMa+*_9I-rNUBs4%T@hbJ9E$ip zqA=o8#FdEOBko4($gs%R$dpKbWL{*6$VVb8L{^D>I=QXSa#ZB_ z$k!rgN4^{RLFC7gt0FfN+)jFzURFA0sQNyBMiJBBOBkIklc~Og^mPM_N+8nhrYH!pxQOBZA zM_r8iHR@*6ooE&9MaM)ZNBg34ql-sB99=%Ta&)cey3tLdn@6{c?h@TQdQkMp=yB0g zqi03G6a9YlN6{;z*GF%Q-W|O!`bhMN=(Ev3Mqi8mBl=#99TO1~ACndnj46mI6;n3m ziI{3J&&D)}c`>F{Ooy26G5unO#*B%X7&AR)PR!hxg)yJRtclqavm@rqn1eA#V@}0f zi1{Vvx0u_pGSZE?Hf_Qf5EI}vv_?#H-m zaeu_!i?`z=;^X7f;)C%8@ulL+#y=5XE&kc~2JtV(w~Fr&-#xxx{LuI@@e|{x$IpqM z8^19AllV39o8ouGe;I!;{%HKE_zUsB#Qzq5J3%Ixgy@8%gv^ATgklM05*|;elu$F_ zxrD|E%@W!sbWZ4%Ffd_6!q|i<2{RMkPFRqzBwGh^S%}C8oeK7T*)W=evO0AJvC$&-POQ~&AJEita9gsRa_0`nLsjsKLl{!Cl zaq9Bab*WoYccp%ndMNe#)WXzDsaH~ePraL_)56kX(^As>X?bZS(jG~xkX9w_>9l%j z&!@FWYoFFNtxwwEv{7l}(_TxPo%U|p2WcOttxDUFwmof6+Wxd}(@v(HOZzG9dfK09 z_tTy9$n=Et^z^Ls2hvNYmrH*#y?T1>^oHq8(_5!^Oz)B2KYdvGE9sNcXQaQGJ}-Sy z`m*%3>6_DcrteMvCjD6Y>GX@~zoy?zzmuUdyo{KPVm#LVfLb28^S#%Uo&4@ zUuR!0-$368-&o%i-%Q`zz6HJ|z7@VteOrB>`@Z%a_8s?~@m=;^_1*IQ?YI2l{y2Xs zelacIU()}mzoNgY{~3RM{|o+>{+IpT{C)jH{GRY| zK+!L$o%jEYUsL-lAOdb6Dv%h+2xJEy3_KKgEbvsIMxaihQQ)OOn?R>P&%l7d@W88q z$${4cZw2ND76+CG)&;f%b_Ko)9146NC=6T*TnYRhxEs{LuwZO3CFl?41xo}U304SJ z2|gXH7kob0BG^9IHP|OOI5-NwbN5entAZPX+k<<8`-9&GPX^Bge+pg? z{u#WF-@A*%5W_GRa07tek;yL@)#>{{7% zvzuf$&u*99CA)X_prYT;o0>f<`pSnY})HTlVhkec4B{Ph_9X{xSPn_8-~z za_pRlocNryoM28tPN|%-IZxzN%Xv1ZLC%Z#J-7}z-E;co49yvnGcjj+&YYaNISX?> z$yt-LDQ8E{mpKP>j^>=oxsY=`=kHuEH!(MuTO#-I+)BAMbDzs?oZBq7Eq;xxSMI>v z5xHY?r{vDeeLHtS?vmUU_zl0E_+7aZxtDT(%e|i$o|l}LomVQaY~B-j)$*RrYmoP1 zUaPzgdEN8+Y1LD>z+nvEbK&n+10sQ2&Ry_W+Wj`rd{CcX}2^76pc|lgO}n zI!xG{W?F&*B0&)YQ9+UfB`Ycl>tX~IG2qXP0Z|MXQB+h6fEX|9taj|;vk_;4J{f~G_Gi3 z(X^uTixw1JRCIaK>Y_D8>x=$Xbbrw!MNbwzU-U}Rn?>&yeN^;i(RW3^6#Y@uQk+xl zEACj_y|_nl|Kif(SaDTxWAWhPlZ#I+KC5_2@r>em#fytCDPC24P4SJzw-w)A{9y58 z#ZMQ%So~V?JH_u8e_H%?@ejqn7iUUZm3T|?O8h0klHMhSC2C2sq_)H?IjLl1$>}8% zN~V^~F1fJe@{-jhYf3hh+*|T+$>Sx@mTWJ1t7LD<$0c8t{JZ4W5>axb)KhwFX{XZs z(w?OQO3O;)rPZaW(jlcIN>3|2yY!sWnWghfmy}*w+EjXN=}o1#m)=wQQ0bP^XG&ix zeZ6#7=?A5sm3~wDW9h-tY+38F+_Da3UCKgbeaec;w6gNDy0R0>hL(*iJH2c|+0?Sx zWfzn!ExWAj%ChUq)|G82ySMD&vd7DwE!$r9M%nJN56eC;`=Lxk+eGuC`O$t+Em|EN z6de^EADt0h7+n!b81+nbvN~Ozt1eQPt1H#3)f?1X)w|RO)JN5=>Na(U`nLL> z`iZ(<{a*b|wbVZ~MQg8h)&g2Dtw4)v39Uv;YbR=_Xk)eU+PT^+?E-D7cA0jicAd6P z+o0X6J*+*hJ*#ck-q3byA8Ma#2ehBGLz>jv=xz1m^sahX@2eNjU*+`e^+O zeUg5jK1W}uU#wrDU!||rZ_)46H|m@9r}P)}SM;6wZv6xOQ+>bwZ~Yhjur7_(hSz9s zbTYad5u>kBWT-~Ms5TmnLB=p+lySN-!I)~yHZC-l87qut<9g#};|}9KW0UcO@tpCp zvD4UN>@&VFzBPU}4jXptm{_~m@v&~PNUUG1Bxb}aV-2xEvEi{Xu`^?nW7A`EV~b+T zV=H4<$8Lz-8oMj@K&E?mZ-&}r2`F-V^%AY8IuKeZlo#lJV z_mzK9{%!fs<%i4dieoC;RUBW@ts+vhx{N@e@X&Xofy%PQlQ z)s?BrA(bO4Ppdq;@|?<eIO2bG^y zepC5l<-y8qRqLwUst#3MszOzLs*0+#s`9G3suQY)R*k9}S2eL}TGjbg3#u-vy1Z(2 z)tajHRsX8Gzv_{yC##;XdZp^ks&}hCs`|3(yQ*KR{-|oH&Z+iQcdYJS-J`mHb!m01 zx~jUddT{m0)u&dURXwG8M)kbv#nqQoud2SL`o`+ps_(9Tu==s;r>kGAey#eQ>i4TZ zt^T_Dhw9&}Gc~Phyft|>{+eJ-@0!9IwI*3pTVvLoR5P;X^qL7ZQ)_0|Tv)TLW<^bN z&Gj`m*W6KaU(KePCu*LndAVk1&7PWlHDATxgm##ap?v%Q*b>r*Kt(#SMLEX~2%j&MI zyRL3s-G;h*>mIIqyzbe$?R9U|?XLTbwAY|s+09?>f6>ISKqZhT;I38xL&WX zsIRXdSU;?Obp09ilj_f_pHsiE{^I&8>aVI_TYpRao%I{*H`hN^|3dw%^>5Yht^c_G ztNMS}|5`8Vk2H81j&11Fkl)a=VL(G!L%gB7A=NOXVMN1e4QDr;(=fAPe#4T6OB}vR+;j@Ns8h&gz*pO{(-I&|hp|MM2sIgCDQKQya z-dNXoLgUcJQH|poCpJ!NJil>4<3){^H?D46)40CzUyb)SKGOJP7^Rr>1A8FHA2>uShqiuTS5cz9W5KdQv39()-e1q`yu7oIaek z&11}V=J94XGh+5LOH9M8G#ku8=5TY2d8RqpoNmrF7n#e=mFCsv4d$)pUFHMkCUf5j zz7zZ>r%8mP-#=V%+sAfHybUI2G)hOMm9_`U6YGaD- zOr@zw(=8g`3CH4G)U-GnHBA@QbiH#68s|GS&M0X3mii?3oM~~fksD`dHQm6^HSlu{ z{9J?cTvRWo#p_8L#f;J@X0+US&WzGvX0(BOG^QDKR*mmyXftY3uYsRy;O82g=c1a< zk2g4vgMhR@Bjq-<7Bk7uCHc7|Kak}4l009M=S%W@Nq0WyJ1m}tjyBR()HKmdHR(Ge z#jG-T(?m}YlJ_2^2Q-7&s3wU7hDwj?<6AOw94CLPN#{*kSzBiFTRoG ztdU7hx}v6;Ihw(XAU1+=v&Zw%9n5r66KFy0aS$gO@=%n)<8s~8| z&Ua{>C82R1K;uk=#vvpcXVqvNdZJ<3HJHf608C3u(r6KoXC@?FM9&!$4e?K*cgt-PJMXw5c(zX)wqa(^?~Lni|tuBc_@fYa9s}C{L?4Ty~Vx z;>~g%tegbJB&Af%avrRl2P@~n%H6@7??5tIZnTjurfI&YO3TGI`IbhvaYli#JEMR~ z&a`M;O3*E;`?$`GqcI`gbf@O38cR=_bww?A$wQ0KSvU21-h<8?(|KdAAtTjRP2HIf z4R1{6jp@9x6hD{Z=TbaaifP8GW*n6XXB0?*GYT4K6g18#X;=tu9DzoUmL)f3KqQG>Y>HRw)NCqBT#<2yA?{6oW{&yO4A_#iGkCP^J^ z2=yTJ&X_8(3XMA^F(*w!V=#AU=K=Ki0ruz`u@Tg9Zh=I)&kKSOja6GO~;tUq~UNA{2jpqHBBo+<7^X+ zvp+OUEY_-Y%2@<1@^gfq)bvpZKugle+C+61NHDEUGCHbBL`LJt3mO{CWcHiHDmZVN z-DDqbuwyV>SHMFXB(z`$(}i{f7rL38u#ANh4bT(lI4sEYLBgM9kT=rjkyhArffJ&1j5RtkMG*n)s*E15q_jEY@6a zPdzmqXXeCt4{>HdoYrEHkAo07a;>JxvSZWEofwQnN~Pzb8oTmzoYxuWb;g-v36?BE zsu+xyOV%{1GnJw-5?Zz1m1NR@D6t=^#%6}30MpXSRGJ7q-XBc_+sG3ocx5c?D(i!q zV%;Qr9aVWujbW4Kq9A%sO(&QVq-y9f0}{M3(nbg$Q^_@CBonyEn@;nlO{O*D!6>m9 zgYhj1B0)69OLFH+IuF1$GCPvI2nOqELQkMPuZ6*LnuHyum3C_|7S@SXxRWTTHn{rE z=7NofN;`tlcPDa}%RLwO5S~!etQ{H~2)&+oq`SDTp4EvaQq7p#b0$L1VIyRomTRV+ zwV+27G}F$Vh?aO|u9zlxxe4O5nxIkAEQRg7da1#Y=09BRv zuCXoGT-9b3)d+53Fcw7)veQS$cJ^+J4`5}iKpeTHIb=(7 zaFFI?K$^mTXccQt18=O+p$nWovx5R#^oZ65Nf!7`rqW>FXdE@vsk3H}pEIv?%2^Y} z&zg8-B%XjVDq$y49Eqn(Nxwi>YJ~Fdq|@X2+OJ2fBzJ6P1IqG>0h$ zXHlYYW({d3n8?&5t;LwmcX*sNjM25mo-yhojkNO}o+DSIGE`TOKEPhn9gOod8Vk4P zN+-paU=(XKp(VHsrY@Ox2%EDAs3gr`xZuYXe6k?m7~2H~s2T-`0Pf5cc91%Yy20Q_ zb#okXRy&F|@c{FP{YaV!0&LHO&&!N@*_lgLpw70r2zC?QHjN~~IJR#aAAm8C^> zjfI;Q;5>rrCJ;CXQ#tz57(Hk%7@+5{PL{_6(~krS7oGW_H~bw*HF_*2q~_@HZe7>L zhAYZJipG(A+6@IVq^KLpanT`7)duA2X)v7; zNz~OhlO>7jCvbGsi!!KIX%P@McOvIIfOwvWEFIz|-n7XCakC0+rVZ99gEo!K4QY|# z;yn(n4EEUuJ4l0!C;}#0XO!YlWcnPXfE8p)uQsnAL&jd`*vMo#)7`QSLnxEP6zOSaNoYtW zOwJCNEbJyH2~3uGlY|x}HD_ovL{`&H)w&@kSG-IP&`iQyxRdx`y4pcu9s&@8Y&wTX zx+~tiKZEU;!89|D=J*`TgW!ayYq)rfp|Zi%6oZq32H{!UNm^yFV>Q@Q84N8A4pj{| zeb2IFFeo&be+HFU5W*A33=TF7&UqUy`s8Gr!QRi{oSVT|&ET?>!7kC@M4Z8%(QvoP ztTMO;WH84Js^uU9O8hgp9EoEjwD%~{EXu+cHJSY`JzdIkjnbsx6y-tZ0W>68CQXDM zX@E(IaP&wKOhzvzCpt`4Sd;S|ChM)qX%CY$9C=iS4`}#tu5Fqe!kb*;G&zztxzcG8 zSyhvQeMF4nOvtcDV{`u$GHVmVlAc^4)Hw2VmgN~x?2k3+^)%B%jCq7 z$p+9QAdP^PXXiA$$1GdY;k z-8>g<47adB;$pqdWmBDzFQlIKU*EjEgJ4$Zo=518=y68iqIqgA;>8G=s82QG+}z9%r-0%rUrvZ*UN3aD-rR5yxQC#az?H zVVJ>bIfKhK2D=`ElOP6D*Wmh|!NAYpu+QMQ#&C0Bto;T@IR>LagYzT?tEj=EZ*T-; zP_P4Bz|ojnpX~PYN88aB{XbsNF8k{*dI9Fk?B{C>of+*Ty zDzaUU9--h&#yHBw-Y9Q6>ZXH8`w^!*)J5Zwdy85Sn4?Yi0_CXD!VLBLgFHsE>1$C=j;fff#ZwOIEz5z>=uo)2sF;L zX^x}NI2%LbOpC@@AR31RXq@@bIFvx+OoYavCmNoJOagj5pBu@z;f)(P5n#nZ5wfPx zE<6sVGCf(mk5ZS#jnktVXFJi5O=e;tYfOyeLmC)`zhih1CC`NiSZv5;p~v%aOc$k< zq2TGRC0RQ0XhKaZ|NC>S;UAwXNn$+6uC&aVwOk(&p0aOunPNQIb)5(m`7KFhxLDa*8cI?`1`Z= z_jL^a_jThR&uahpY&8Dko`258O2#Qv%@t6_cR=Q}P^uYYRql3KAqYr+DWZ$$K|dcDKsMajIbv#7Vs=2T zk7%=GCi%zb*wN3p=Wq64&cCl~e+jWO``_17iN8Oa|M=XX9=(o& z(?1j@@z)w4!@O`fgiR%XVsF8*>InsV4vuYekBZr}}0%!yXO9 zV*5d+9=EWTk*P0r@E$p(2@!Qb>j#6N^GJ^q5!_lA*`MqI4H}Oe{i2=rOT~@0h_I zg(NFV$Pl+M2?%xI7G5C5P3Sp8qj5Hkh6(9PIq?=3-N%`NZrPCF2EKE)iKCVd`_VW% zLgUa3jk6XsZ1yNxK#!!3&wZlD(-Le)kCn@53p7oBO*@HQ#0jA9)Gg$C3K72 z3@qWI9h#vYL`tcXNWlmu9V9Yo>f||-=T4nlI&r3RkH)cpmTGjqoHup)q{;3#EWsFy zS;Q4@jSqBaoTfxmI)5S$pX);ovk7NDTnwm2^yT_JEH!I26Fo{h!ohN9WoTISXe;P32bo!XUKJOe{h?t6rd6ZI z0zg?X@RLU2f;ij~>9Jc+)YvldflTDqX=MpyKasOx3$K!1psYaisBF6us2MqT&?>p~ zgF9#md{h%dk$u6hZk*+*Z244zE4Y)tBdtP@7euxdck*J%sKT4^m>iv8d4D-|tdT#{ zLjSsq)&BJvOaALK*8SIKES^8p4*t4~ZT$5ad->}#7X8;}to*OfaGigB#vcCu%$PXA zm>@=$4(TZ(Rg8=pdbH&jM}9Fjurac9_>PDZXCaA^ae^Z!hQ{2B9M;7+o@-$6-N4hv zNcpq`d15^042H9+#Md~*Er3F^m& z3lgJren4mc!(y-L>|AvA6)NwRgF_rzqOItRcUhw}owvzJ7LCsap#6^!5$SGFz$VC}! zZVmEHIH*SyG?-%s`6gVX<>H5Y&dLn(O=yUaeDoVV(gr^IjULg{VBQ+!n{bimBj1D` z?TF7T;#fXU%ZxJ^a2O064CbG~+%zcpga>$=TykTT$IlhWDY+*JSo3kwnFxnhoEgwK zv_|6yEgENIXvit46xpChPM0hDIAOw%(*lA2_;Few?sPVd#vw8qXVYjLBBSB8AU*~< z#^lalmHukZ2#-Pq;9y3j!P3JG-XXh`Ck4%&E znZ^hFa6Z%c2)v!oG(Hfr^VwZDId7E`Rru`%(}^m@=rNrLW~0Y+qJSPfrW0Xr^q3az zn3S5rMWzMC#OOH!fQIKI&<*>M}SlW4OmgIT6mxK}o<7 zMjR8T*)^_;X#BX!`8d`j^gA*MFhpYz29U9M|@ehtPwt!l@XHR9u(l9(cr*OC1(J-IIIGZi3FNErn_4{-POZd?+O^+64&@hrkv z6X(pD>q4-!O0XrZG6vGfiXcHj{L_zC!yK(f{fmfYFf=hZ`ZXw!jT#JM38PYe6D`vDwfQgvEDkf%u0iDj((1Ih5f{2W}zctWjKqBL&11*9EynY@HMJ z8lMg`jvlh$E%6WUR5lZu8=n%!f%dw)CdoiSo&xH7;(ba3wjzcr#7wHy*13rUXB?<~e0-ZC%hI?v(?TSvA3OQ%C zsczMVBR!p95Hir}d9&usnmKhYi{13eQ(UQoC7_WF_Y4V{TQHMp?3TS)&2^XUoU2yd z!$l0R-1DfKo1mv-WNAJ>2Dx@-fJ8a_!bQ?B_gITY5C=z;Y0oN`9!kQ>su{={Gwlylu4 zJ)pbBiI=1@_f=-Q>RLKJaHDZ!aN5jZ>DCyO!h15K9aoBJx#5(W&W=?hyAGPM5#>6I z#z!ACR}$Ux5M%{GK^AgGd7NCtFCmzMj0aU>4lX+EKtte?e}RFXvp+PBSfg>KMdOZ1 z%jKVl;36&e=n*XTnwndV=dzdPmeM$wH{4%maA8g7dWpt(SLJLSpZdWbnA4OY*W7~_ zI*|vtV8R_b|2&4{4%kvsew~j58=Qkv`P7fbRXUw*u*w#};AEJ}wIbCus^lF&YqoI| z#Go|g5CpR8Py*jE`?*@GyNCPSBkHWE8jGUlCNlWYw&tGyW7$>7s$oo8wZ`Q+l{1cf zcniu;b`x(EaTvgLB!~-L)R@x!FoOoekGPx#;%K5H*JvE;jmDuT8b=?{IK~)_qm*bIdZKYShQ<+n zG>&qiaex3C$7-N)=!wSB95jxiqv81oovA54&ywQvEGa%woT980zT^4$kV}dvh>JWQ zQ4l?zkE|4WJRboh^mslp{zy;o{@l`uTM!}lj_-JXWKq!L{qbqb6#xDz#fL6a{QIjE zpS?^Gk?{cUk3b%Jyg$-Zq(hjVe0U_qheuL;cqB#a$1S{S;xu}^YGzKFe-57J-+`z3 zTwj_*19*&{OY?Jh*@r{jG(TsOn8JV&|4j1Q=n=89*?cGJz=^Xw)u6(ED3V>X%$RQa$ieof^p5Vz1eP2vgw4{^n0KA22MlL=`O zA#n>)(4>jbBf&Ij6b$CDA0dU?I71_w>x_xUc@B;902;RJWP8vfN6SAeK+h2=G~{;q z_XOyXzvYrUdJcKexO;Hq1%)DR+)5pVr#PG85E%_WPJ)Tdm%}Rj@|PDuB88$Mt&`92 z0~*mS{IfmuXe$i%@KaO1lPgQA>rA<_#6d1@p&jviG|;0(s9ae}Fjh<#pPXuSkCEq2cAp$RXV$dI6X*4)pFi*1S###{aLy~Y=~L?$ zoOfa8;4R`ET6JqR68|UpC-9c*4_jqhm$$yR_2aE~wf+%rq+Zx&X`AJE>-73IkF|NT z%`a^v-Z9!&B!O>RIHu z2=8@%&~s2}r6@{IWi;N){DShC@~!f-BD^KuQQnKZw|QUie&YQWZ&dy*w@>cm+$(b* z&V4NR{an$uN81?Qf;hQyHt&VGt zx9pZ3XB=05+=<7H#rt)q9=Gtg<;OK2cO%}GJN5W`@b23|c*pHUc%$v1PI{;EPU%jA zJ1y;WIo?tG2;NKkQ>TNS+jllPpWb;{=cdjNc76+Qlnwev`{(=b^S|uh?f>5Yv;Pmg z88+0VXO}X(6Lv_Kab0G1xv0z4U2f@eFW&yTr^}~Z{*8COdb@V)+PkaXwZ7|MyytaN z*YmqB=z3Y#=B{hH-hwy0ZtAwQd&lk@y5HITzWiwZ)_^}SBJg@JKe#L?@V3#LLNA4j z!)wEt@DaS-^Q_48JpytXdgx#xX7AMbT+uTH(D^jh5O?OuC&_vxMPePQoc z`}q4z?6bMgpM3}So!$4@eoDWS`<>r!UBA!!eT}z2&gj3O|IPj18E`z_<9OYG-356C zLkmtVxUt~Yf_;UZ3qysKc!%O6gk)K^+rI=pll-X!>R>5kGb%G#F&@vgw>WjB;PS@wA}7g6rm=%vvI zqPwHNMh~go)oOLTxSbzZ_Ybpbimv4G-HS{**Fhx#k&>n!h6T~F4j7h9|L&98}A0jPQu&n7RRoO-5=W) z`ylpXymh>5yd;kJCq6trIlds?9KR{PG5%b9SA2i`54@u;kSIyiCQeRFOiW2!m{^%u zhd0q}!h7i6Nqn04Ht`4EKG!+fI|<8?9G*NQIW4&$d3kb8@?XhElFujKz+2`%PJWj> zT;8UKE5! z)_c|~;+mHA9-n{E_%WlyGPm?w*&eUREtc(Nw}_}8>v?nYOWVUDZ%3&Z5D>v1M6T!- z!CH?^hkSnd=TgNguut@e-HKIco#>8DXFQyREP8+VQ`v-*YMOFS z8-L$}XGDf=8Td@^rd&C=N%odK<#qXa*36*N$}fxM{G_~XmE0uug~WUjJuFWAIwDW} zT10z_`2p)#af!Ii+ORURN^DAq+hmcLpKC`Hq05(c%1h;9@li;05toOpE%tDGi&tK5 z$S(L-kQ8;|VX^oPaj7uGOJSc#hvWlR38u7b6rW#oI{2gTiFUnzl$~V9-i5NA^c^e| z9V4L03fTewC#TuT@4mGe7}GFox3CA zE$_tTCjW}C>^oz{EB#iCI37fs^(eCu_)ztzWn1ONKleeBoe_oBaS5*N$D@M61@ z7kd~P@{3_15f9mYl+4ri>3;EyBHLT1d+csXUS%lHFMEC0Q?xlXEZY9&seF3LC-3JU zlJmW|KEIbzx1^$STv&MedW7%A{i2l!<>&cJPKnE2vfrq$zHJUm40gFVMaIQ;{5wU& z#pTW6Jiq9?S&>nzVNxhh)N~mX`d5A2Qh1@2tvBw4MQSwvSZ0D|yG|895R9P>6PKvpwbQ5z&iQ|Se%W$qNY?8hD z%Gm)qU&arL@k=9iLa~S4Aa=`z){20&Qamj#wS3Ky8$-V5Lmv6`W8x|~$SMk0y~Jj5 zme}5mRm_onMR)o9YjWfEZnF2r(a$!&m-}tg!5yMoKn&PlF<=9f_`B?}elfOLw1djk z1w`%dGDi+?iX6$eYO+(jA}gne*>bO3Aj|Q^31)bqxYI5SW?NYhP+@ktJ>&y{tr}l; zjX$H=hSkx2Y)+`9%u;Q?^~AVP*dEg5)Q~R~%4Ecu(B;fU!R%g7%ehuV=3Fm2p5o7N z)lS1)SZT#))wr_nu1g$?Wz~whU!7HjgV_q_hK^n#mnzArD774jH4@INf8uBFQV z(xca}eIz21-lZ2@F)MJ&g1fgz#1?s-e8MXSi`-h#H|*OH%G_yd&b-)3(EDQ0qx-EH zBV<qDL;#i zoBcD>oGtL!>y?)IM*^PN*?^55i4&_~dcW%xvTKgcrL+oqrunTCVbo5M8L`=(_+m)R z6-Cl3N516f_5w(m+~E<=D`K{&67yx1M?9^_7cv3=sZBFSPAKg6Mc;tTZIo^LL`0v7 zp6Bm<@~QAIdv(z%AiVEEOIkHY@@9rwwuo7w%o6c9RvZ-zLcT8lr9sKd%$wsE%`hOn zMdw!|x5_3f2~%!Yx7@Hy5yy{^9S(%-yX+gp-SU3%dqf1pw-Z7>@*X8(t+-4azCE%( zBu|%qQ3=U>0@khqUL*QTA6fFRh}BOKVxhc4EVcUv>?v}K{J=iXK1Xbl`>YuepKZzs z@*?@Rd`FHK7g?rd3bf)Km>^uYO&D=V;7mnU50f1R_6&>R@)(ck36X6lridZ=aAy_r zK(<236Ay~3<%2%!F$;p2(ZJR=L1mP`NsgW`Tg!p7bHysV=7{3GIh4)Ehbi6<$lhi@ zQ^bM$#4*Bb7Q1p`2u>EoqOTYt2Fqfw(7oGJZ$V4 zCq?omhJO3-fqS2goF!&^E}1=T$(ey6Q#b8|2LG`C4bkyctVm9liWmxCe4EI@y3P?3 zF-oJn=gUyUmn*9`icZBM9q@VN^Rpmw-9mY(P_|SNHJH-_XV>H3MY1S-WQ3(=YrH}~ zfXihf_*-1ohK~%gsQIPSI{~vv27wqwvKScNDi0IlRkNHD(4#0@E?jgH2M`ZUV z(Ygl=k1wb+IR42VqsTo+KKCqEl4C4yz|zGgakbSGRz5q+S(AL+%CT$g963eq z6;piHp_zVhq3Hj!=>2}=Mqht9;*`i@vB6e-0eiEp+8tXopZ$dU0WM?nj)-q{1ReB_2CS>a$5v@(2%Je~k=;XFZYU@sL$+h2@>%ZoB;{BbQ%%TEtqBneMR%D)!8l z5z>@5C*@g9a(Z{G@(Mp3NH@_(j18;qi|N8^G#W4}=BrNWbcUv7^esS$h&xP&F zT4s8z6BKJ^W`szIo4*ietrl4KiOupyajjhxuxsUO@*%rCVi`B{R{BAsM_?=cXe-W^ zMqo3}7M-n(4P|Zs-m+pr*~%~OfyZftew`z7rUh+Dtd{yo$@tq%x93Wz1TtuASEQ$bTP7gq2`awj_YKqA5&9VR= z$AP!vUNFaiKuwZWqEyc7S2AhT*^w2>y<(2XdQdEAS)&ZIKNDA;>Dlt|zI)yd2;ZlT zWu?g>GDkC>eCLvgyi<`UUnhn_Zq^6#7K59xdiRM5A_xo>DEeNeZ;7n+iu_v-d?9@K zqCwV)9&)X`9>_mmj<^i`P~N!a(uPJT_FKo6%p8Xnwg4?lb$|R zTi&=lA|FxYh-+Z7flHi)-=Umubrm1V86qDj@mH8A{3o5yxOTVI)g$jv?y|ee55){j zb+PzO)=2yZ=3Ojs&65{Yp8J2aQ^JI66DK%ZK35TQt*IV6SFt~~pYRW8+Fvbl0>bmv zzMq>TKQ$R2OHV*5vC4wxNVd%?e^Jxt)uLTMwE60TpPD1TH5vP4n}BSWsO%4iYuzYz zTER@BIg-(uEjwi0)RZe^4ut4{Y!J7K2BC`!q@GF1 zLyCQM%RrCy{nhUQF+Zn#+w|g>qHjR-+8!^3MSV^Ymp93gP@wGr`HDP6*2~LbxPoFE zY~|5-&R%4H>=$i+_!hp*Q}m5&E87*pyvSqlHvD#xyqy1yfzEroi#1yjBN5qIf7<5+ zJ?Dzdks;R7Y%8>_tR>kKL?)PNqnJV2*Dt=#yy6kPTV8?J`6eWH%5%hGYp_)#CW$M= zZn0d{%8;D@V8jRbl$|~!6!ffdVuB@Nv3=M&g#T67_2NJEB^vP}@LESjdJ55`kdtUfnkBXvz=>D^4E4soyWY3efr3qVH zCw`IF1wGaFqe{yU!R&LM?A&Yp_6zG3Yg6Xm2tpx$hrGe;#Tf*t1>WosyQ62J_lVP= zi*YW?V*c!9atT7VpX5&-pUD08JBOB7Nv`x36D_|l6<(mU@}?m}WHcb-!{7cWBJWn@ zsbUo5_(g;vBO%+THp5Z`J(Gn9W)c0qp7Yn|y#LnO`__f5TUs7o2YwNu{H68BC0YEd+aWXPflot?`b7V0y5Sc_+U^I;($BAuHUToPe<&*t9{~W;0nKe zvhr9kJH=zQfkz8wUyvUJJ@ran&=Y|q09dUjeEV3aWwtLnH0a&yx0626@w0uu{*wGy zc9dNzEBpHprg!fld-$x$jzL={mIZwF^k&4<7fXEfTNA_=;!5#_6RoU~r-{k3X-n=p zubB3vC=n+$Ba9Zio8-%4nXLtEUA`=r$=$eD&I2qPDrTOQyVNVMnjpu>$<1<2uKc1& zd?~NACIrNu9AHW$6hys91Il%V+Z+o++F7P$gNz{UFg}v)p93Z800P8eO4}pN(Fk?S zgUiz4RgZ(()Iqi`4J8}ye|1>IdI-_-7TM-sa3TZq?+(e2#C`rrqRj&$clj+%S(O>%>HURhJ6u!*tPWy<*k+v? zxz}qAmcy(z_LX*xyi?w6Ul6v+`uKNx#nPttvAJ;pF>SA$Bv(dedF7eSC&_c1=yA?S zF-@GfGQwE>KhN|G>M0`PtQnC^J4K!~qbJCd4#-{B0lyfx@vsPq69S^~F!XrbMxqCx zda=0Jn%*3_D*%jy-2Zp+dp}RSU&-^{WJ(Y>nG=`wXtPBa!ukcF$)9V zDejVJ_PT4h| zhkV&ZS32p6u<#CmboLePeuS6rD%<@o`yxK@_K{t~!sxR7smI@VY3uk?hMqBgXjqQ_88N|P+?V~#zdd9p=HvFbcgVP{JKlWy>75Z# z<&{w=Wv)z011XnQA_@_ca0%xr_5;=fK$T0f6-O$vRoM!;Of0eQw;%B2iNz2!%L9PC zDI#uY`AL3cL2RyD0d0=SEdjX?VdNE79lX#~u|mA%3$|PK-{la+rpa=FXtcWp?2x<> zQQotTg;^x}$^x-%n7D0pH<3U4wXv&fa-*vT43-4}S={eUas2ra-~W^z0dvm-o_xqE zA@V;4J!yGXvC>EG@MmhpQ(keEXfK2EY{2XGvIk(Wugiaz*IOIntcn&!SN$}1I6xcS6p!B@`-`?@CN`qe3|z|rsX|rNwy5wA-e_f z>^o}|@v?Qi=LRKDc79d(0YsYvJ}H~U6e#QK0kIef1fLkVE3)2a6(K)5Rlx9z51Qnq z;v8Un$04nh*`Q~ElIIm89uf=1Dv|u$2R~;uWq$C8rAJ!H1=i=wk^7jVY^&wxdHUQFW zmN)y#nj`if&3>Pq7qHKhm-vJPG30We2qSfHcEkq;MG2;;&5bNmb6HAS+M z~} ztkj*NT>RQ(U6h+Sz1f~-UEH!M(DJLj+`hl%sisKFE6vtYdsgP#z!}+~U#`;;pPY-h zb3wB>9+BsqfVlPp;P%T9(%p3xA|JUbkY}}#Zy-qd9)b1cG9@1z9=5{gd49X^!26;k zAo_mRAUj4@cn9=aCT~AKEMJ}_@9ryq$cNXTAxp*9uvi#WUizPNGChLXNq*V2iAAEc{1M8Hy+bb{M4t#2)KVItbnEk!{a%QwitF zATlNkkV<&q?^Hq&0v~+{boJPf=pfQLgx)8D)s;Y%CxtcgZIC#|p&Jiiln#djEfE)J*Fd9v*W z*=k~FdFIIfqUeT_*nh~h+k3>Tb{or7!zbw%%42Tt^Izr?zyI=e(c#rxEAf+fRZ>6jhoUdW0?35>{Sopjxbfh?2j3(&$&QATuJM85_%kqm0tV8kwWI9(^oBY-x zae;kE33?aE3yyYLuEiJbU3pFwu!EfGmE}b{zHN%^Z{EL8)C5G)!Eya=j96ug*s>JV zsF6nkb|3ke)Z`-&f7#^2Uu3yHM%9 zL>nnb1msEmKK*W0L>#|bgkKSb0WlM4edQ~Zg8t{EWTySOA?H6B-mPLLfX&)S_I9~V zYD>JY&1e)z9S2Zq7z2Ru8wy+_p~s6v-G^Vl@nGa;*`Ubd)YFh8>U_#;PXg3#Jl!Kp zHj6kSi~9rDW%B(pXdV^`p}&K4bx3sI3ze*rHT45f`OM4K&hm@zH^6%h+%9exXN2sb z;u86!)$_FQWK@OzLV!+E`VDab($dpKne-f#qi=KaGm+bb_iNDy(WV?BqViF>NzQ_E znsG%KMYi{@{^mKM1VpQM$JA6MN6Oa zjLSD6@*g5AQGP_A_R68RB5M^nwgi}^rhn?8y>JQJ6*=Kvak5N`O#$&4N&_>IW0)lG zLqN9?f^xZEbpI*Z^RkF`d9m&!ojXTAKJ>@;pNh^P@FSva$Uo@JvB)Rn7Je;{Mb)_o zXsc61q{n(*+x+6TusHTj-Kjcv|Anf~M~2%Q{rx_x7M=bT3TKMC^#8OPBGmDVeZTw~ z--ocab9r^YaNgEX%WFV#!OR448VVsV29>biK2ENYC&|r@Lpa|f+CCxLye4`D09^v0 z{K81nk&Uvg*S<~ck~g4^eJW(_J!_`5SUxUZwr7OJzmJ^cmyMUeQT31~L!MD8=q8W> z_GK;z=D-7vZu(RXxI;GP+N!+9QkD6#vJ&~gQ&F%kX)b}=>y>YJQf!~NK`sI}rl3H3 zz#1HW#A}@ZT=j2zk`(e@P6&@j+hr-AU6BwE?$2Z?my(cYyJ8=!$IF+<*xOM=it6$y>k{X zo`ZU}Q#L!i&8o>Pz11s^c}a9kiZKE4v$$W}YuyCk_&9*$R}TDDdyI)J%%l|ykO+zR zjflQeNU2^_F3tu@zV-`oD!kVPNXgdA`@(sde~IG(PFID3-e0=}k*4{sW%dn9W^}g4 zSD}{6jwC$6%!{J!8>o0$$&990@$5j)_B*#ex;t?9vsW~&jl3Yf@Z5S*&8?{b0_J?g ze3z9)U&CqMHvU-$gg_XOU4YUGY7=-u-N0Z2gt_<+RG#Ie6Ji| zW5`8eyR%m;`p`fgLeBBp%f;gZtpVY@{-J5TLhj)V-!Ec--yXV7{3+kEaL8bdu&uVB z+GtSi1H0kj$nD-tSw``QH-nk4<)7KLi@o+c_GXWKTg>soS&c^qGvodCjF(ZNzRQ{!ur^z&Q}WA1?LuT; zpR(_eyX9|o`*9I#@G6g(wFM_FI*P>sB<|0Vi|xV4p-hrj;GX5O7FK!wG|=VkkOwuA z&PWK2L|$W3NGAFNBMp@&L|SIFB#?Oq+cT@|6zaH{YrWPYWcqHmx;90uZp}bn_6YV2FgQNW&@wouw+G4x=Gi z32B4Nd}~_$LD~5CUVlWL;Y18yx$lQvk3?=jV(8jz=fAcZG_mH&|9h*W5uKhn9buKU z2SnDee!E5u8>m#RlotibqCNOU}m>$&HJgE^Pjl|gwC&f+{Kk3+1vF+Zc)<+#UQEq2PS z*6ee`_7hg6^_15-`w8)qc+y&dJG^<hg%-^BprJG%{#y*$tKmA<>7 zO-Iq8yzD(W4h4@$q_6A`nL9EGS&&J}k(t&l{%F(tDUllxZ9m*|V0Gl%Rh92b1;w)3 zdbK%X{n3Q)_nD$CDj55A9cYdmXsX>Sy#d*_I)xen82$iGt#lQGMNoQxYWm+Ex!x=M z8$SP4DEXoeM|}A3{lE>1Fhw^H3K-g99EwQg100P6LVX_ieUdDd zYaM_kT7R?yE;HwwQAK5q!(~o1Y%jLEd-9$RIXRvQ;#RmQ6Yl9hE1-Zz*$&o&iI8~G z|F00ijYy`b+5dk54FVsi42$3WVxEE`_d1W<=A_^^oafKpl)VWqzG;Hsv}C%Uq|xhP~w2FsmDmA;Ew} zYLlFW0hd` z_Z;hEd@Y&|$pHMI)(eO3nj(46I7!&QjvEM(y$pvWp4s@u4}nMjb=ei`B6o_fJ!_|o zxqe7MPM72Q%9qO{@|>14j-FM(+Fw-UR2*URW!s1a_MEi}>b$Ky0D8e6g;!|#VhjqV zrSQ7z5H9V8@#lD|^LD?}!dScc69UDFqi5FB3c0{Z` z!JL^M`Jp1uY?x>|xteRlXHt~v6t$RliZPfITd zfsAH)c^r^l+|tLHcR;3(N4|zsBxU{WdB{6mEw;2A`5%)1S&w)GNwY_+dC1w_7z!TS z4>@4_ezC;K0aF&Z0^oI#^#F3f!Gj+umXjcMYKBM_3Hp4sXV54LOtBHSt-gLJoSMOtr?GI-UP$#Be`xxa{3O_5u@2b=Fa zC_I5rx12l}3CBqair;y#i{qg_?EyxnH-#5Eq01<-Qf|k!UUH%6WnZtzb44#*OuRPy z!Bx-P_|V$Z#lmi@md#u-D=_N3Ti=hs{*UtJq2`?D-{8nCrnkIrgz|zpmu7nT|8X1% z36JeoZzmxr>LZAidU=X)R^dQT`^p)|& z!;pMe?gy?_kVi_$b~s>?v#MXE>>ZHrf|tWX)&NHszE;J3a+p{WhK&^w92#w7x0ZYJ z@oXFUCxDtB7WiVASojT`&6hZ>DZf5|NbMxK(D?!utQ4;2Pw`>C*d^K^i60iaGZgUU zk|;9D-{J|;{gb^&k+s@s%Dk}s4Nz#lS2W`QK`)&1sVJ$E%9-J1)~)jCVD>?=-6`sJ z{}2xx`VaTxc#Y_T8d!;JT`zrSgfBwvbjy)7&i%-lbx@j$E+{jGiMI0iZX&$%XVJA; z)Z{+y5TKia-0EP@z4CdlG;GtseVY)|q5Bf!0U6l~X#;Xv^4&*_ejv{u_!;`q@fyV+{8uCkn* z-X{BMd$oPFwHi_KCJP7$|C&$@hxJ_~e-?lmIK2E(5C^*$ZFu&eC+CZ25V!PJWS5C0 zb&0SzCLzlA|0se@!su?@F2C^0m^FM-NPJ`u^oZ_Gw){&u7G<1A5neSS^hy06&fWto zill2BzFV0dY;;6mFwg@?Hlc`$0xF0BBjzJhh`OREVnS3fW6lXNuB)zL&F-3W zTFhbBSe*v{(>=Q3+4p(h@A@xgy82XgrLL|zb?Th^H1Qrst6c|`R8`bIUTP0wnhK~5 z8~YfVp0tuD*|gT|32|NXrFrZbJ1n)<74pvW?4Y(-{Yjf`qEjZ;oIR!u`ePG2%d1DF z*_3LrNxjIE2F)kE10P7g=<0=C=-({*s%E9p0YVow&5IVMF7%IR^v61@UD#~>RCqVr zVN*ZKH_sU>s8pS$F6S@Ul3b*#(n+h*T`Q+6phlUcgGw(`elMx43r}j))#zP6C)sAL zw9e`|^{Q8@`m0I0#X3j{k}EOkN7B^SqREq2x> z&E~cJ@?t4Ro^8^m^IA3SnMo}yJ#%P)AEWQ_@*S3~1(=wuA8_7~NRi*9zT?%?S`pJ2 zxf@Nq7M{e$Gl=26#g36;O=ES!LfRa%eg|n;D#xalVn5RX*I=!kH>vV4Qu3dpHP-5= z2h{~yP#PT~^Q>Jeb0tnD!HYDUO3~U9@1nkEuSgaA6%DWPs;s^vwb*wwV8*le#CFpk zDW#5QL~~Jou#&!aWwi&}$M%v;t{Y49X3)RaFxG?0*hOx&kUEhF>yT8L-i4T^ zu4CIY(yfVCE5-!H`Ap=GNDEAgJNrcGvT3j@>P38O>7g`g2H7n6C9KLp`K|m`Ij9`8(>4EK zK3(m`Ri>+Q(1$c8N1r-HlD2fOd)KqKp7m+YHkwkt9XNf2ms%->g|N+Ch*iBnoHbogi0q)YDj#Piq>7{E$Z(5o%+Bu?#juOD_-=u>z}Mj2Ml!BS?vBf z=68Hc%9O3%bJ$f=R=rp4@rEuRDyy-hBn@}6y@O3gqtEIw-ubM>*$aA8I7-(~{M zaXcf1XkHbFpL5m6&_rG?l%HX@=4Y6sWS+Q?1X4(ns@oH2hez_Shxaplep~eDA_HyS z9>i5HA68E_`Oq1~M7GkVuW=7~5=t0cIL&d?ZTH?O^26w%YchS1pR$HwRP<4L0KOp<6`-6qADw2pd# z`$VU6>UvVoRTE9>Vlz9)nwrvS@8RM}ClI1?N;JiY%05v!BAVjqmrhVg9iC*N^Ym@S zzeFSILwe6TZ}FCR3!3hoQUky87RFYfKk+<$9QxB*8FtpBj@6H=J$WgFbU@aV0F5rB zyd=453d#Eh%ZgR5V5jrn7+H}+iPb+zZ=9;IWPp`znnB-Gf=sR6#pYT5lI%c+-4!%n>! zLnFe4tyiw7ByiZNHZZBP%xp2MZko^QT6OY3eZ0qr{7U&3QSK_mxKxh6bnDKAOL=KN zzg>)rjBZemv=1$Eh*|2Lre0=UweP(G)c6U*={&$?b+R+tVP4RqW)|^@Z%9V(qVA@a zZ=#mBH%E+$38PFFZnp-j z$&$QXRc?VX}Sr zdLlT8?NKjFz4g!7WHIkcPv7TFs|~*|JvouyNY6A@-C?q}l1$b}c_Eg@LjD2uuyTO( zVPe=iU4br=hNp3mryuC4RcKz3?nrmEJ5ruT|LFE>kO+57F4`$kS!w!~r>m(eeN)%9 z4_5Y?`k)zmMjhpJQg31Y4xQDi>~g1(8t2p5tj32VkeU+}G@K`Xj&_0jX+|*Lv8rjJ z6u{E-h50q9PL5~ebX87zPixfzCbg@Xt^Gu{W~9iK%jiJ2sFq6wF7+fW6a7E!hg`Nd zj3wz(+4O|GgC~)<`H3!!b++c8H zz$Js518y<6-Qa!%cM9Aia4HH|QJ@qG1foDC6o^ED`Y6x>1v;ZZUlbUF0;wo48wFOO zz#bGViGo#7um%d&M8S9z?0|v;Q7{Pw=b_+g6r=e8%|~6R0&R1 z;S>X>MsR8ar@3&-h0_5z9fs3!n7m-}fvF5k;V{*NDITVlFr9+w0!)`-x&gi<_yF*s z;3L4tf^Q7I4fyupyMiAKemMAX;FH161fKzZIrvrJH-i5a{4Mamfqw!1cbH8ud%+wC zb48dVV2*{k5zH-M9uD&Yn3u!69_Br8w!*n8oNK_j9-I^4+!4;b;5-b@XgQXcP9bg#<%P3eT!;%KeLRePAvJIAfuzVzAeiU^^QFj#eM$r%yt%RagQ8W@o z8=+_$6zz_pLr`=)il(CIA{1SNqWe(v9Ex5;(T6Dd21Ql40`4!G`v>rZe!2iMDRy$jc8aD59`4aImAD}!R;DApFm`k>fo z6q|%%sVKG(#rC7vk0^Eu#m=JGWfXgbV!xx?g?=30QW?=_l5gVxQ~PTBDgPs`wFD;Dx3Jn^^@Fu6tQBF6 zgtZ>5jbKfHwF9iZU>yqUSXigRIv3U@u&#u46Rf$g9)a~Ntk+i@ntBUjpA!jdcqhYq4!pDAy#?Ms!TS?@ zqTtg2J~QEy4xbh9*#w^*@HqgVlkm9&pS$pR4j)RUz_%cLi@?_(zSZFy4c}P!j)U)1 z_|AgwBKT&*cO!gr;QJ$dPr>&RN(Z2H8I-P$(sfX}1xj~E>2Fbb21?IE=_SMrp!9zD zxx&v1KVSHjgI_iH#lo*K{My2A0sMZ3-#PfZ!@oHEec@jj{*B?^4gO={pAP>`@IL_m zv+%zP{}=H80RMahI3vIf0U`pbAfP4!>LFkh0wyA0E&^5|U^fDOMZhftJVAhlKsN-o zL|_jDK0@FV1WE`hfFK@0VF-#sP;CUoA*d;WzClno1Pw>fcmz#D&_V>QM9?M#?MBdX z1YJPT4Fo+x&?^LeLNJfuq6jXI;L->#hv2FRu8rV^2%dxBOax~mcoTwi5PS&1M-lu$ zU%o5JC|ek1JyEt4%6g-$AIjE7+3_g5 zA7vk)TmzJAiEBlHwPA0YG(R0Jxzp`sruc0k4cs5l7~H=^P`RQwqg z@1f#TRQ!ZWCRFl7rIM)RgGzy@G!T`(L#6LgX$~sQN2Q~v^cIy=gqaZLiZCyP1tKg0 zVX+9Shp=xD)&XG)5SERwQ>Yw-%6(8d3zeUt@=H|yfXXVuO$fIjyadAI5Z(mg%@N)X z;hhoQ8{s1nJ`v$_5xyAVYZ1N;;d>E&7~!W8{wueC#noYm65129aYj$B@x`)d!&ZNK_w->Jw4@XVj>G8VgWk1!`gXW z#tYQ=1Ca@cT!zSOL~cf84kCX*eH`j+(7e zGZ8h{qUJ-?e2iL6QENDAjYF+u)S8Z3J5lR*)UJTq>ri_$YUiQ$C&YvxW-MYhAm$Qc z?jq(XV%{P~MXVEIT@dSzSTDquM{ERQqY*m>v6~Q^i`c`6J&oAQh`oc@-w^u}vF}mG z33ZC1jyvj%MxEbK=L70!hzmhn7~-N3R~K>35SN0ud5BwyxHYI7fVxXi&l&Z~qh1*5 zO+dY^s9zlQ51_t`1|ewB4h_1a!5}pF4h_CXgIQ>hi3Y3CU>zE4M}s|Ra1ad+BVItf zJK{ye`ysv@;=>RhiTF6gPeyzy;P5w zjebF+D`*^w#+A`H3XR92@vmrn8;u{M@$YCXqe($DDS{?eH1R=`5Hu-|Ce_d+22C2G zNh>tTLX$OUvK>thpvfCFd55O{Xxa!($DrvpG~I`$KcVS4G`)tV_tEqjnl(qWHfYu! z%_gGRTr@j?W{1)2XCy=-p*9lgBVjw5mq7CXG!H}b+GySc&D*1SZ!{l`=Ba4D5Y0ED z`5`pFgyv7sTtN#9T9iSHx@ge?E%u_tA+$J$7T3`-0xhG_vKdJ(48EBP>R{PNE5L%Z*>*i?vJz7sk>ol~!f!6QP#uII#(Pkjp%tV{{XtND% zo}sNX+7?0EIJBLGwr9}x9=@rEZ{DJv1?@c0PDDFDv>S=`1<~G&_Jz^j6YWc*eR;Hx zK>OinKLPFcq5Ubezlrw0q5Vg6Fr!0BbSQ@o(df_&9eSZd5<1L4hYWOBiw--{;TSrc zM~BS&^Ka<< z6rJCKj8GGukZ3_-2_*U&(Y;Qx@hRi zqpJm72czp}bc;i`2I$rT-A16>Z|L3#-8Z89U37nl?$6QvExOC-Q2;#z^l(QH5j_IY zqXK$Fqeo-(XpJ5n(W57N3`CC+=rJBW7<%?W&jIK;6g|hHXEJ)uM$b(2%tp`6=$VV2 zC(!dMdfrFRKhUcHdU>E%5PDTduSV$A4ZX&o*G%+UgkBrbD+j%fqIU>-Z$s~$=)D)c zFQE_6Cl-C`qt6`l$v~e>^x1+wJJCl&Un}}HMc<+5J05+Pq3_TjAxk85fi#&!T?Mdh6$@NVLc{n$Amo~i;*M~l3bAFh9oZ}`5~zslERS`g`_%2 zYJ{W~Na~2B?nvr~q+v)JjiiZ4nuerVNLq-bWk_0&r0q!Bi=@LyI)#ZNF);}fGcoZx zCcebPcbL=wle%Ei0Zg`F@-R#ui^)?kISrGyVDcVJzJ|&7G5IMbzr|z)-xtL9h4H;7 zzW2fR!T3H5-_O958ko`yQ`%z6P)te2lr&7qz?9{fvH?@JV#+Q|Ify9_Fy%R>C`b-M za&siNM{-Xj4?*%+Bu_!|1|;u7@-Za;j;SRv)dy3{U}`v~*1*)pm^uPelQDHZrY^F z1QLhog)rS6)4eb~5YsDQdIYA&V0t4=Z-wdIF}**gCt><*Oy7>__b~l4X7HF%1Tz+4 z#tWp@MCw7LzQN3in7J2eWsuezX*-d&AG1O*Ycysp!>lWqrC_!nWf zMR&01F&4c+CXY-DGAkf68ku#G*%+Cvkl7KL-I3W3nM0BJ9Wo~(a~d*dBXc1#mm_l> zGPfaf4>AuS^8_-_Bl9XU?;`UtGATnlvUp?_MOF!9`64R>S(T7g9a%BRibqy+WVJ(9 zS7h}=*0;!-f~;A{T7;}@WNk#&PGs#z)=^}gM%J&$x{0iZ$a;>fcUW8yi%Vf~B`n^8 z#YeIDA{M{G5)MnOSW*c~=3!}JEIo^5E?CwR%kr?iE|wp~@-tX|8O!fr`C}}9gXIj% zKVyXnD=b)194maWq6}74#)>GcsD~9zv7!xD48)4rSTP?f7GuR~tk{ATyRqURR-D9& zcUWP^N;6g#!^$wMtcH~_SlI|G2V><7tlW#0hp_S)R=z@ZQDobYorvr{$R3955y<`y z+2fF%gzQPkPDXYLvQv>g3)yp#oq_B{$X<-><;Y%z?A2IR2CJg5su5NtU{x!uO2n!& zSalhz?qSt)ta^`C`B-hn>JY3B$Lg9`9go$6v3fpMAH?cMSp5pCKVc1rH7-~ahBZ-G zGY)IcVy!pU24QU_tc}FlIIL}owQaC=BG%5p+H|a4inZ&ob_doTz}jP2dlu_}b!Mz9 ziFH0$7l?JySl0#XMqynV)*Z*XGgx;K>#kzm6RcCQJ_PHhVf`$u{~7BaVnZ)%SbzjLk2xr4F`aVasJ~4aL^B z*g6VZQ?NAyTlZq?C2W0-ZH2Kd6x$kN+hlCpi*1Ln?HIP5#zeYoKeV0M$SCstV2!?at@Xk6a+P2y#myHwd|vky{(N zjgk8ea(f_m2y(|FcN%i%BX=cow;*>Pa!(-l5^}F2SHm72dj#z9!=51QX^cIyv6si* za@e~8d$(ZkZtUHMy+2~_8SE>AeKoKz6Z^ffzcTi>#Qx6Me+LK3;J|SlxQ+wA;ed>T zPB>T&2h;I`89#Kx54Uh=BYyP3j{*2`0Djz#9~ln!z~NIkd<91u;mAN7Nyd>=IPw@r zUf`$&M|-Mfv2SrK3CCvO*by8%hvPhsd*Zl=v_$MbOfHI9G8iIO zxG)PB@^MkX#bUTP5*J6~;tX80+c~)XBW|C@?c2DWhug1l`#tWo z#GP)q(+hV-;!Za1Y{8wwxbqY4sJPo3cOTPz?`a z@L(t&jKqVnc<=@fKH{N(hmm-gi-%Y6@CF{f!J_~?8j43*_^mB|TYIBd-_o1|V-3^3ssE7D@alJR68o*c!Kvv_g?PkzT!6Q0`eG!jqO;^{#=y^N>N@$@~O z6~QxiJX?zAet13(&(GueV?2L{=Q3Ut#ET+$(FiZv;Kc&GSb-N0@Iu4OVtDC`m*IF> zA1~YCQ}sagjdh;+Jx8scwGUnlkuA2O<%lOkKfyZv?%{*f z5^p=$s zrQp+fe7b^9x50{mC4;R4+X|Kob`0zi7=siCX%VEKAd8UeL+$~259G&CYC`D?WfhbY zP$Z~sP~)KXf|>&L1k_W|DnrYLb{qCY*pDE;B=S2Te+lweB7Y6?FXMB0e2&29IDBq` z&*M0Z<~TQw^W->Rj%&zq-8n9a<4$tiC$2z8uD}Y8bSMi>;0k7O1$T1=&vFHya0Tt0 zQwZnOigTL4Ic?yau5(TbXR5}T+H$6J&h(b!<2gQ!GdpqSv7Gr4=j_8dCv$}gaD|3* zh0bt=7$=xGp*Sbh;e>BFVIC(uqb29jk@M)uc?{+}zT-S5aUL@`k95u> zoAcPgdFW z>CgE*=X^hNr31Or)40;lIlo$*-(b#fHs`mA^V`Mwec=2HaQ?2Gzc1%soAd9)`FH31 zS9AV5IR8VO|2ZxoiVKM20^+%VQCz@DF5nIq@RAFR;Q|+Nffu;In_S={E~q9Kw3Z8c z#07_Q!F#yidtC5+F8Bo({6TbLY_HwJDi*3QWr&{+5Hgd~Y~eqCEEcIoAi+%1k9Jx# zZS@tT8j&`+x#)bRj=+|PO1mh*5nEm~Wd~6#B@CmWpOH{>S}i*7t7%g+MR|;o^0+}) z28+(g8w4UN*-?U4S~Sb+DPo5(n~$$NQZ=tE-OZMI-jRjrC@ED03wFZyENo=4!STT8GU>-4li8@%%ccSy1eYPM`Y53(4d$wqr z(%r~g4H5a>T^!Gp7WwT74r-9duW$L~@qcvOpBV?*x)5f;ZsSP`9be(-Bd6_Rp&LKg z?2|>gX_TM_i~NJtUt+e3h3=-?>`7!g79|W6oqNvuQh*L7)LJmWg6NU2HUILmMRmRs z9BI_bK1+1E9cq-Ae@J;%(IARyC{e=$K&W~*ACfTIok=-cW{6DzKTSa++gCW89Z zUunLiR5UQ$ZW1={daW3N$!K-R_==+FN3ZVRz6Tl%q^%k5FWB8gVS}TmzEJY(h(e&F z$G%ctQW{6UeW8fT6iVdizpsgoP$EY!eodr?(TDVp-jpheAF5NK^+X}{AH6EIF(UF4 zM8Qk%-9PI`RVQd|X`#rM*EIx5C*=xZYfl%*Sm4ME-DH4%m0lu$+nx%S5Uu_0QSVtM;2# zLUvw78Yrt+(7*MkEOLrGgC5N<%Ek$jCnK#;*BLv=OgYkvu(?Tf@;I488|6EGq4?0g9t z(b4J*Q%0-ia}tKxv_#VKy~z%#l?n4y3bCp@E7*{fs8b0(*@1z#cyVfKmN!+FmC~zB z-N8P$5G+dL-$hd}DL69HKi9j^OkH?Ol)shx^Nb+Xj<8fgxzEA~m689KrX6Wz8o8(o z#X>G2Ha)`@qPZ2lV+v*FH*f@`>2@={Ya*#9@)uPV^bqTx-!zaynSyA}Wv(=ujEl(U ziDt?vr>tP1KWPu^5G3e<9jt?&9#Gs8^hl`$>+P%Gi%v_W41Ze=3)RVT!#E*_EXtIM ze_I#+XTv}v`$@8!Q5HMRI)v*Puy*Y!gJ7E;t7qMt7Lr0zfR$b4)y=)X7N}Dz=v1<4 zdSy@*LHu|i(5Pv9@#F2XRH0y-QZ!bl$gyO3?rT)<2^$)1RKHvwqqJBS{g=*ZuJBdg zR9dHc?KB=eAPxFAxNojt0Q<5wMv@~^-vlFYQ0m%J&sA~Lp`NFRA3Uf~(UfdVf1~*r zHPe(IK`L(Sxe+F1z-uhiBp)M0q)t35ElnVGU_uqK@yu5mZz7CKSsli!_f^@d`fB4% z1mHTJW=8u%t6Ex{V45UN;4P$PX#gr_lCD}~O-DMC365lZegjn&O+6`|UZa#Pmv10w z>(x87KD3eUHxSgyCv8%D={KTwSBqB^ly4P#1HtZ+Yg0-qn;QuEVQTS8f>KlYnNmDh zDJVUZ*AzcROQs3!XSoICFiY!O-6pM!UJ$b6spMYSgy{7e< z($AR;y+hf1zHGGW*-EYXFL1EGaJT=C$E_ob1+sJG|KKSpY3eH9a(U6bPlxSJojY?* z>Re~`BkL>FZ=6oRAgStLKea=U&mFRUir%j_BQWnFtSo5}H#_b#*=$r(oi0E6Ur^*7 zX=aK}+x~CZKqIZzQvQRcH|hX;hA6pI6^vjh(d0oh9Z|KH@*gxMQfRm6w9wxs4QWOr zVwmV$i@K_;RNwxYFw{=o4QqN-f?}t@ST`Sn8r0WdGatD4EjpQn^-| z6Im+t*e3j9ljgp1^vpTEXQ;pW3iXUGnW+x-RXc|G+#~b2*aK<{J+X4b6_FG$VRLqkMi zF*R2Y#!x)9mx5AXQ4p!YRapHp)MUDNvLbvJGPV8_OJ(t$63qFl;w6nPMh!8ND`!}< z|Me{yspRUi-ph_Bz&h!xtmBS=Q}h|faU=c0`Vt%NNOn?WbF5)AT2xX?d#Qn~9z7pn zQ&Uq3wWTKe(TklVTb8_d(n>e&yN)p2A{R)jD=I}=7o5jKugm=mdO%5m{s6i)f@@tvYezb4&8A>#b5S^x#5$wpNR+m;z zl@aoT^ht^!UX?sbb#)~Ldx}n@D9vbno)VqLD}H5!Q1<*QA1gYoQm7cRS|^f&lT|v& zgNgFJKA9=eax>3if|jn&W?w2MUy|~Orc4z-w)3Y+pE`l~MMuTH5|t(f%lNg1U&-=n zgKzv&*{@WIav;|6ORc|Bh*1Jo*k60eH$?MQtmQj~7ORN0m(5^AiFFN?fu6@4Z$6{Y-!i-p>J9d>HG zXsSxIOoQeacY3wH_D-nqrOPyb<##H+AaVQEZCadik+O9c`6Jbh3jh7(sgJ@1i~d6v z4#b7&At_82=~b$aey0!9%2aJES%?t6q4r!=X_a)ENX&#PJ@05EiyQ~qAX&faIlM5q zB_oSio!2u$(K@rIhvel6lqV(I+sC*nKj_S!5wj$gQvA*)353=2rwl+ZMKo1=ZZjLX zOIP9?85Y+0JR_qvI-{qDZnbxmFkENJbb@rrm^7KQbfA?Ga?JB+9S}9)JOa{oY;0f_(<lZv~ z+0V>YUo%-~0wib(1EN|V`SN{QnZCe&60;Ib1@P6hj5mT(v_Jh#wn@*fZIhWy>@ALm^$hw8k~M}n0QwQtnZ|^uhx3z-Dbqn7U=JXk zMW^om$!1(9QY?w#h(tK$`aEX@U-dVxgp*01n?do@Zs{9Z(LA4~>i@7pWvhABCHM

ph?w#*#bLAer$ICL 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

u1KkF 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

DT(5aapxSs(B4j;wg+|$U6X_qO9M(0kG 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 TPC*|5bC*mUF3)9hi~D~7@evju literal 0 HcmV?d00001 diff --git a/src/qt/res/fonts/Inter-Medium.otf b/src/qt/res/fonts/Inter-Medium.otf new file mode 100644 index 0000000000000000000000000000000000000000..4f8033a019a75d8d7743a4aabeb06989b5030f97 GIT binary patch literal 269160 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�RXjb}Kg0m(?Y z`tJqgb5@tXopE{z`%&3!c+!<}w7d$@72nRgR3o?fet5mvV45%(0;X6PB$;7J#@SKN zno_y|c)5?nQ$1p>^to*}36kB|B$*b!!_O%I^jB>Q^~sxDH3rg1Nsgq2fjMTB=K||n z%T)I^?$V`Rt!}%&ySnGzW%30gfqeJTT@^f48nHTqizmC2bp zEWwV@l45R!T_gNHBSRl}T1~Ry@&02!9Ddw`Z|aAK?M4y-ZDbv{8__WK;1+gmSrzii`8%`9cTQbMn2 ze{YQ|HDGTY&KIB$8)4Rk$x=P6c`?}PtigoG*EP;o)sLw+-W~Z-qF-3FO@+B_3(y3I z01T}TytWv(YZ>^RhbeUKD)pgRP@B+2{+LGcGgv?EHft%is>0ASTH%~#w6tB?0G{Fr zzXRZISMB0rH*Ep^P>uZ9sX)iJp+BvLp`&G63jMCy!_19hfH~GdrsGgf3pmZ_&#PGv z7e|-OV${_id;<6prSzmJeoWM%6Y9WV1;y?^q6JiHm3_?t;*`gw-s&xP=!EtkuGOG9 ze%fcRaJ*__iOfwO0&6Hua6+u6v0cCi(<1#}b(qZ9kV?DpiS>2|v(Rq5l6|`J9`6fg z4HiNZF6-LaT>jtM~j^Ydv~H4?U8+;14)mB<;M*?nJjxcEx_`HR-7|m z*(*JOCdR2E5m?I=>0J(Z(iJ-qp$_#-cjixkfCw5>tw;LHRe}~;RfqDeZg2? z|Eehq1{uTQH{lC)!IJjDcQ^R2j zivuy(9uL@lycnWJR-_E@)ta5MxM|NXe*RjEtZ0?Ao_Kh;t*9Kmx#DB0}=ll|>hpx$ODqUocnp1?OssBWjMRI@uLzW}prbyDYHNKazv*OlR5ZT3g6V%dWNnN2VR8N^VfxclaeL0#?l8InB8K6HOV8^Ap-_-d$%14MG*qQ z)G@a@=YwrA21=qPv$!TQ0)y-iZqfnl#=6=WjnvGd$1m>4kF7RcX0 zb9FNt)peV5M19q+ADTyrZkbIzfoxz4?FU;ZoXyIKY+{^~EjR($tb7z5^;Crkb4E%Q zbJiQ{#R^4C)j6G6T&139CbXm<0y`ci#(G4gitjg54p?i&a;7|71r5X8b_WK4wBJm` zNJyl0eyV0?^x?h}sQ0H}A@LLMSM`Wrf+wB(t5ReQ&WD|JToTMjxGHj@cdAnav|+yr zAYExZQ{1LXPgFbZAT~RTM3hfd!^HKy=qCn(K6u5hhHcGCUS{X-2;zupeG$y(!5-BE z7T$jP8n9;)dTtM#<<3C=?Bi_M!S2CVty(XmXe-JO`c>?m`*A(o)bUT)s@F?2m@zrQ z9F~3&&lQm#K^G$IXq8JByieJIyF@3wdO+2E0`V2N5FPQG(UhRV-lRUJ&>2Za5@?ih zSY?_ue9E6h)3qDe<9;@s4!J~rK0zsVwE!f-_s!~1f<TOJiHvi^b@=ojkY73G0$Pqdo`IN1bONzMlaP1}6qu|HF3_T zeMoFz>c@kpg8(s&YXkN^0-#N!L9c*XWS$$K#Ttlty?Bf5GT5hKXSau+W9kA-`Nfex z18p#X3l9XB)Ba3o9`^oG85`O1Uh1Y%iyIQKqqH|9{t)19U|%-i*xaDg(jDu0E`R7F zUP#X{Wb}xB7!F^n>&=Gnge1q~UVOm81Zy^7%7oMPeSW+87ij$^0mYHD)-Ox5E*g~0 zfA6|`HWh$xMe{PJc+lXXs;IW!ag}%*t`bW^vEl(XG>p7)E4c!g1nLAjgIYhaGp%#c zr_kq)()b@x(4il1dU*$RhWvWBSm4{)eWi0sVDzJyE+)AbzF32*Gfcx67$2KOtIV}oz!Y=dr zdg@BuH_>JnT%q*g_`HQ#AL6pU(J0^GN1SE@f8c@}l#gf^51_VuW`m1HqordtJ4+W> zpq#U7dtu-|8pKdvmjEq{;CB0gX}6WD|4yKsEvd~uRF_k@q;8OZQpNaCvI2m-qiD=6 z?Z)!gPNY*J5pS_0QZ3~#KR`YU`F>LEBBA0+I9hZ8 zZ=$a{TDTSXF)9r}u@e3k&daTa2k5H~*rFc?$9RYBm*nJcxQ|_CiLuajgS-Py<1lqT zkDSfgU?@7%PL9dXD`)4P26Z4quI)erg@f>_hA>( z41IhLa!(HQz<8RXW9j33b&Ik(pwgTL{llyD$)i)h^I;=g9v}S=;Nm9GE-iA7*#mgk zDH~y@)^U1;mubMU@Y`O)G1SDV$D#MNaaSnAWYTupY#GP-DLMLf}=wHV3@vy&O^U( z9@^^jc6#)GdeMrOg1`wz_L2y*yQr-J@6HDKt~>XDIGTq)2Zm5rSI&cp;|W5Q_T3Ml z9TcS*K3FEjRVqLc1}MIB5!wsLM#F2|+lOubB}855T@auyBG!P1I8UA4C#iRDZ|~$j zGy4X(7S00m*rM9KpU@@VAWHc#skAr`&ol7`tk>r#rE_z91v#6??bZ1rwS-a2!0z=UrLE(VWS5?Zo6P|Jdp>U7Q|$g=Q+c^^01Ku@Yrg zX#iy{#c}(MU{kdw9IcwGR4&$)jS@W22NmQ@Ze?~3nu4Fcc>OHCz+pnL}4tx-RR)g z1~m>vsk{pFbUajK%H>rHHwQkZbV{LLDW(xM^3fCIYX@|=8#dQD`QuXt4>_T5w(Tvi zJVP9{XWwIvEAnl8+0_ABbqLXY8UfzT#jAW!3zV(MQAJS!pQ1Z(>U4)2Xmq42R{P#| zX`j7TYP(OHpF=bIk(l8b*mP8V%n=_FP$U{rFF>Q}D^CWT>cx>>jwn_dx`84kA5m=Q z0B@+buJQh$xM--OHAI$WfHE|5J z&p;Zip5{*!EN}zqnM$pLy%oBmQU|lQBH)LrL+WvB978KG5MS(R`b5E9!5{e(PeY`I zPi>46bSnpa;=w^fS=?Gx?2kGtbM{lu;siwWfu35tOc|lpq-BcEA4O47HIbL%gqhtc z<0+)>q*yTPB4Mkl_Kl)dp?=!YJBujIGSCD@m=AW?2L9g$N|Ib_ZJ4{^BM)T*Op*S&S6+*4?*%%b!KHusQtNA z(IGJ~GhT&y7yoW`^U!s*7-Kdc4V#{lDmS&)I#POFu~q@UkLrexxhakTcedxzYB568 ziR;wf4yb&=3_4uF3&Eg*V{yQ4?)eGV#dP()EVYPe4&9wfRRsv8sIt1Qt~FaCf}3t- z2Zz7C)}%(CqDduOd^VXTay3!NClY`LmJ?~_8GKXa#?v^qD!20iiFofm#ly?r&zo=D z{N6>wERaCkzbMJkI9H})^~Q;=P)mlxz{hZk+($idY5kp~yDs`B87%NBw%2Dj!BF^w z4^A}b23C)mPhJ92jfDSc7p5l6OD)ZDd@?xBhL6%E<~W6S2FC#dZjW8`sPJ#FC$Qi74x-a{Q%XizA2 zL`1M)M$a40JOa{64m|=+>9Fv&A*Wa0v{)+t6Q9AU|!_2^*1K2s=hep2TEdnDl||^^6+8F z!#cnPB)n#w#5;m6Et4Hj(X(IBt+RbD4!$-rrXtT#C|bRWr=7NOQ_?0GlW*Ge8Pj~C zysotQw%)GtEAq>5eBI$d0h)QPMs8QAj&7CraC(ZW1DEHsytdG*^KXle)+;{zo2{IWA<()`>QXF(nPN9 z*cYrhEi&@C(K0+!MI?Yh_d6wT72-o(o|9YXM;~J zZw(ZzJlsxq?iB;axYT?Wvb z%pCCBzhLr)=Yn4do19*nlZ)U=Kptb-1wK9rI@DnRl0Oh{lZ=;|xRnh4aaH@3FR|vd zDK!lm$2WMB#-1UZy&uPX=;B5AaCcMGCd#zh6}qOaZrNl}Z6B73DsX9sDUK>}VHnDG zrp#C0Ts8_oiq8d)6+SDAy-zA0rkVp4&hUsb*9P4is;jY}^HwoV;G4$!^Te)WX9Px+ zVh@1bmaubs=FXTi$Ez#twKk;X|(uW!7w>wwa}>DTWE%wLJOL=D)$eBKEW&5a~O3e!Qn3@fQ|L(#XzVSov?~&G^RFOvW>K}csWS}!|D>aj#u5P{$AS9 zdvE+YRj(d)rVrX^78oFp0&gMC8m z5G@Q&RP9`tiZ_WIwBRlz+URG7{Ur@GUD}+Dn`dtJZP_$^>Lw$Y0g(k^tnsG4n|{6m zRHulHNkh>PDx3{p%32!ir=wtvkFl%H4#1+S48FX_kif)ajmlVsJabgAv5G*-Y6ap4 zKvmv9-LP9nnK@~95WbdnTON;@9_jSRZl;6P+4#5Mh?MqXF3kZu<|d;sPehBOsntq|WNZ2@@wyoDi%N>>BORa-*rR$T`~mr5PMB*+0*E zAG1VJKrDMj4)7OallcqUZD)Y>IL9@qSl$?`o)ec9)1>9PH5IJZ>1wIqeEPDBa_I&I zzNXARnmQd0lk4&S?$(B`jW?qjK0X*R?Y!^-Q{UT)GGf) z>;1j%+g3Udeg-(t+h>;Bu5E!GkY>DFQp=oH&h~es(q?==cQ-1G*>JRI99VE&E-S?$DwRcTb!tUX zZ4n$WGcr;sk#n~EohvKHFVxYp_DtpuSU5JIE>Ja6z<}&tN})$8@1+;Y&*b`%>JrA> z8bn#^%aJW}At;0xL$R+EjN;5tAW$nWR@D}GT^IQr*fkyC45;2yU^O022=EwKUls>s zFwM(%X*N$2&_&xK##20wv$o?bK5RMR0hMZ=If~9LL2l#NF2vWTaP{lVo>K2uA-b+f6T zKNai5{h4rd@v}_y_v^~35%_Ce3~E*6K~ukY{O*c{`!*ut>0a3kJ>syu-KuX@^zhCR z>Qw%;PEM{A@4_BC4FuSNJk-lKvHA`a>FFT?Cbc1ZQ9xS8^%~0s3UeVJJTnEr5UWY~ zetSdjeQ$shsG`xzOrg2B07$f>Wb>{d>?;jfs#2@fr?AvU2jECS0UT9}CVA;P_(Y$8 zhx8Du6s(y?!>U-NUW+LIZ&b(!G_629NMsf3pgDze8>7aL8aH~}0)l<(A{AENR-tP0 z0RmPf4-bK*SgWB8v0;x6K&PgF^;nB1c@cLwi9c~3V6}XJ#o-4S!}*;DD8>ceK+3Fm z9?dTN!dUXzk_C$wjAMiwS%m14x&n#IAM}D7{D|In2fgMp^C4wKg1)yaQ5Phv9#$KV ztyx7N_>2$c(%)ty50*KWQa5x6>mmadDg)I)~Q(skAo9d+$&EHI{SjqQR6i`79zLLE@bd7Fib0z z;$Z>a4Hf^5lH3O#Lp8e)IGR2Y3$GI1`?4J_zcoWwme|Mk$YVaTlB$7d?#asO&9!;p^UU%eB*(u zuk}_nIM2Nj!RtenXgzY2Haqeu4>IGf3{V{5E<3;V$Qs_>|L&LJ;%A&SoUUWGg?o9V zrns#~)Ia=(2bg0-#=X#;I*A8v9$=A`NU(PsAZmahILST0JVT>ArU&jigC5n+$`iS- zt=MpP!F@$m1BPDkl9!BiJsA!r0XvN%(4o)>Jz_joN?AKn@2ZzcS-<{X8kxL?YS4f? z?;3C?+yF4S-nHObxCNsAf4>EyK3V|er5D0^^F+}RvvUhDw@!-Asi_}n6m;2l@K-D_ zKSJY!AIZks&v1*F$IU&!Nm{8?;5=o$L+=GR@1F{!*G#zp161-NTWXQmn>0tkIBriV zEfC_?ISf4(?c`x(XeH&K2$xE@+5*5W45I`~r= zOR!%(ia+MmC>)0t613s=dHfZ-f?q$vnP~CzG5q8b@gu5%oJXbn<~Ypr<&bdBTKB5> zL$AiVWe#0QgM$VC{s2Y1affn>o8f|Tp&;bW_0K!=h#zqs_nQrzD~MV*8Hv2&0d4WX zQ}K9?7{Y7o@#kJXSx``&aIL<0j%^xgg{z_xu7or$=jss~rzBHH(Pr@nw zI?B#0EP?;!hYRa~XUYIg$e+kKbEuO!RKvF9=PmMzX9}W*$RFetnV~3;1WvZztoxat z%Ia!0vo01MAa<8bsNKKStd4d= zi_fZFF4kA0ruIN8U{^Q5&mO7wg$3zcj^;c?v=9m_15!$KGyShk9F5E z99`-dj{XT+Yg%&BC_p~zuc^=aY+bnJh?f1OOyOaEOMMtUVp8~N^oUWZk0U0AAIIGJ zf9i4Im{VzD{pLYbG#+O~u%OKtg~j0$F-o_dB8Z>jQ6o26lQ3*OFttmuSHe(fB`o#m z8(8YWuiX+`>WC9$Jd$f3zHTqFSAeRvBGd`L3&jfi65@(oLVS;f_UBe7{4TIRM+L4w zEl_t>saWbGmBlLU4MrmHD|i_uKE#74aI&dd*{1mAuV_96yp7-8d>=x~YKHyFmvDFp zb?!{ct5Rlo(W54C!$~`oi@T3p`@;*{RnP6%05s|F;1}{>i=IQg8gWHg| z^W64Ta}(N1OQ4vz4h8K1VBt%*1t8^c=0TjMIB1kuP4Z)3Tpx09e#(d6IQpPCXNF5L zh(b$y0hwvXTYY>T1{6#9dbn62i-m}_1O;E`t=J>9qq}txfAjMkMU8)*uSB=eA0nM1Sy79{af$UQBPO$~iGO zgz|_1YS3$AkY;{j?}8NSDs`Lltu@LnuCd^w4M_)MlD^sZ`NL$_(`^Hxk&RO+U*4Ns6~;GJlH$tG z;;301ajhc754}4k`sdM``nB~oZ2eK*_-jH-W>?GAg8wY1qH_U`l?ym1~?i4Z6pv^C32^QSqc?zQsGF_fQGU*{%EMeK`V}fvoYeL8M4o6_G!NoxNeC*AqUD{5$Xyi&+L1z&X~fJY4iG;xIT%hdyL(wFu~16Bix9H;SB zpoJQY4I1O&Gt*#-estvY31d8&9&V&{Xj`&-RUa#D_g%kz`6|I#9EfQ!lSLSVl57J@8gTaKn0#`VPZSc0&}nBS2FCJl{cA6{C_jfJT%E#A;lo5`6R|aH(?G z{2|D@j+c?eO7YNC>l%&o+b2ajU0q5C{lRiH)kG)?f9M)QDMpUfBQ{0RX3Sl|?2u0J zRA1N;Fm9j1>qG;CT-Guvb9`)0G?P7)K@|<4y@Wlyh=ASO7j^>!!2|(VY|^zfG9SOE zD>Ie?xj((sDfr%$!ZxcaxU_bEt4k z3Z|6KS0UonC{T&wTm;XkcY*7t@ex z?Afzr=a!vce7L3i-cByKKXp-M@U}nkVdyOYKzR*X)CIWd8a}fs*Z``W!D!#9f3N-@ z_TABYXO~@|EW(LcCD5fu_+X!Q*#joAj(unaZ0yt4Dg9I6NZY;5Hh1aQv0Lk=&0Qsn z=4>Fh{}ot=AyGL5vM3V;ckf<+vgO#5by+>EUbgOdiCK<{Y*7GVQ46PScMUtxdPJM$26gq2U628yiTZqYt*6^KJ?yOdMoN; z_tY#AAect}0KSqDoaQyeA}}!1W1gsXfgAhmdSEBLwG+V}Xm%b-kqGt&oGjp34e_v= zWBcjShS}6?uADBtBPU0Ac~g?S&YxpbP2D_=e(d=wnbIQ$6b;3{tw}CQ`=VE)A3=f2 z2(&Q8BaVeYxdSeE*h;ZaSlDbWRLQ5CO+5U?$8J`fR3PAetWYxV=5=y_Axo5fG+P( zooWc4bh!#;k=aW7gF0nGk$7)h9vL$a)*UQoL7sg|Y5k!gFaSc)F1LLaPLJ^|*6{~53qC5+(5G;$KtH0o z^zL9z`d*%DjEl6!R4Bn8%uDHUt%BQbVY)3W#kVoBjuW{>ZcMbFB6Q4M2q9zacxbrV zLO=PVL0u!bQXCwZZO0-7prg#`#kRaY<%OuS9Ckaox%X5+ChG6A-<*@9G|T;3 z1#{=DEhQBNWJZW)mEKm%og*$01gSv$>1R{HU3O9tHwxo=)}K7!%Z8bVCupK*_ybtr>(_Yd7WC{vWsg--+Ah_kH)u>_;h6GC91D#||$dbdM?kWonP+pFy$4Y$zv1;eN zgmStmsXw+T04Br~mD&#hySxl9mlU6l{Fz-4 zZc1MUsA3vUTaoh`;z$H%%y;#VKOSGfOqcCZjXe2_R^e=D&?NClcisQa9qLB)qy{Pm zDFI>&xRvr;S91uh^@qCcz;8#l{h?T9{Yn%4_J`IE@H;MuO!fyZyc1J8Da2cB6Xb=(CfhQ}R5*wkT%n^T7!GKEJSPNj}IYg%~F z@tg3V!)@U)XS>2M_X`W z>S)8I@Mx3b_W!BThC;)mjo*Yvn;r3gceH^A6dr7{;{Tb!CQJUmJlN2!;lU>B{{O>Z zvpnIkhS@AU)Q|{|v|zUINW*V>FV+jXN_q9{~qrwiB0PoN=k%Q|E#-Y=_R z%c+M8h3jD}ToD63S`PDplA!vu2MsJA2+Yx(twEl)n7(Py16W>H`2G9#<(3DegSz@d z0G;LJ(LT6ps0$9ypSBmvSYDc8OrzQ!&Qkm*N;AcHJ@v(Xsg=w0tSlV!7bwM9uxR0F z_{n;=Y`v^=CG-zom{w=QTBUID&*j`|th@IoDPA~hsEwV=L~ID^V0tdc%8zjrJ6O)F zg@#~bqi}XF*T915g%uuEi1=Fe*cNkMf46-@^yI1@kP$c11?ciGz&NZmZvwTkIi1Hu zc*FV-EX>c`_D!gDx6=xLXa!WJy{JACA_a*Ce%X!IsBkx0DF8@iFIp+Txn&<(lbk-( zBMyZ-(V7(QL~By`by+Fl2ec-I-;eHU9&VH#vGIS|DqTgKBBxop+CQ~jx*DC@Fquki zS%uV=HGkK#=BX{Kk=nA+sV%FK+Ois{Eo+|IvKsGNmJRqe9CG!34l&8a6+A5==8g$n zL%HxLubZ7HogX?}vF?BQrC_oB2R`U`tuc@4+Rll%yS3Ap zOrdME3YxrZu+AFgw({t%n7=NjHffV?j>om%;BH4=t1hdBgQ7(^_eys?bk14L-!=1) zbHz-Dc;_OUblH#_EBjh7Uh_aTGBQBWF^z&3gO*_Bt^_F5ibn=QOAh#-=;pZcZl0u@ z&pf0Zru5ht1@>UCrv*5T##(zMy{xx`w4eq;i>U)tjQ`LhUduX$CqqnWKudi<9nze7 za?vPwkh-mPW3QW zm({DumtIRbF6Mj=R^eb!W3`Tq@)}*^H7XCfDGl;mcnyWN=RBo#>)M3B8d3!fnGad$ z?RUSOHws^*bd4ksmYdRj_xOTQ$T^Pp0oSP@+f@baN`d)jnns0c2OcMK`tLZmGzCx7 zxJYf|*E+E$B{nx7q?!MunHOJEl#j9__GM}_>*c_vJ9~B6Of%YPrpyw~;q;>#&fz?W z#5LuU;9-&FtbCFPvYJb%;ZvBW$Q=imTJ_{EWVreYJ2BB3o`in0S9vJ0KVD&ap6Y|q zMmd-@H0QjJ@=%Lz237B=yd1i#OABw+a&4hlO<(it--ZTwJP09;*MqXE(QF@priQR4 zXiYiu{>lw^8eD6al52J69Vfik@9uj{>bDJREd}l8X|NB*1^7{KJfPPH;VmKv7L<}v zch`s4mxk~;R}mvge%VDrUqIf1&9$Ck<{m;z|_E3N*8o` z))x&JgDBL4p(zhv)LpOnv;RANb0OGYK7n~JXb*;qD7cW55mfA3;y9q8);ME+?8Hq8 zJ_FA|-Co|GSEL}iTdZ6Dy1s|o*L(1-r|yH7({Dtc>3-0C|Il6M;HdN=+?!+9g+;Eo z`IGK)CCJ1tP9ii=vJDZFDmWW)LsX!@?LcMCLruVsE=&p7dbI~^mB5X;36AnmdxJ_u zoDb)AP5uf2^u!1!t_v423&R!AOn>k>{W#y#6>|N5#~=LCy9i8FMpUQrenJ?gQhpn* zH1OEEB_E&Q(?29VrrK^*z%Wz+;2s%*_r&u>+%}w0;E{B*z0Omde$dUfK`$mdPwwUk zm>A-lJwl-7=_;&bD}(8>4%1a-dNnfdiJL2wZ~UpjrMx9d_2P7a6t1s8 z&MO4?unrZ^aVmbPvEm2d0cm779QlC%4{h%O9mUaY3lr4c!vMmm1!#JD5IGBxbCN&= ziJWsV7(~uF=Rtr0fiS^fvW*QGFgcl=P0j{vFxh}fr@E_p{O_6({=WO}{qKEmt@oBd z>fF`c6;7SA&)zxtCwhS<#W0`pHT~mF@c9NKN>C1PiO+rrcFM|0)RXw$F0z~cVo8Lif=Y~77Z>}EYm)d zdWZ31HQrLG;E^#`UL&qi_-bB>$-z;js^{hfOu!fa1SXGq2GYr_f;vW)&kd!!&g#|T z&yWmOtG(CB!*Yr!wjQKpztTol=$x;e+rY@RXfp;Ml9e*V%C!3Xi|Qy&82VUT zB*)S2R9apHXHe`vr)0w>cHf$<(|mP?_0WDAc#g6r1fJN>6zicn16eAD%>nW6ZViM^ zp8eE*KErva(0s6y${jQKTzOpJN#L0xi|+yqM%DRzb(ZrmfP<0jmtf1C&O>C3;9`m* zF9zOS2!87U+-{cjP~r0p8x=)?!sT3fq%ChB9()U{#!?xtk!2n-;>S{0@Zsg7h6lHW zPw9R=_+mgbi>yoNOkVH?@&uw#sb+%n5)XR_RqsIXMOAmgbUVt<1&Bo*@u&DxF|;ch z!vd1l>ja|gr~rNtLYWC7!Xlb@#{L8_sE!<<-pYIH0A|FCKM_4fg;s&z`I!zg4Os7= zL4>RMKueWMVXDkMt!jXyi8SXP|IOcH{a`@uq4T9IRYg+RVUS%rE@r-Ie^8%4q zTNVZH+69L*ERIAPA+Lp>VLn($sXR;t2e5g1NKUAED|5TiZlr2;bS0Vf1 z!}r1BhYy4sr+uJ@*WK1a+GR%`_gV|B4#=Nth_*I|R$_d|pZpPqI}$bNjo})P3S>|( zd^dyEhW4%m%=J6qHw|?@(mf1&5Q>y`QqB)2Ilmfgyl(w!D6_24A;GI)IC8Eb(%w@B z;8HDgqruBkib4Buy{SOKq&jkI;53S`2v^$%FgvDBhTT>lX;pxHi8|7$fDPh_o~P{H zH*fFVee>pCx!ifmmdhgprsQ=8qp@;voLt0jT-^eI7+K`_XQxB5^843F;cfLDhP3rs z?6`3fW{gb$_#qwkNoWhAaRA2XwD~(X-z-m|3f}tGGu?Xfog2rfzFDYO5|t_8H6$1fueTN z4fqJxMcgH?7>f@y1v9#lZ1*n8aV(DKrMx0e#DT>V z*U^F6#H!rP^Fr>_teD_Rm0?!*rUQlg~y5f@cOO zbEd-`nj;5-M>)VaG+lUR0Wa^*iOrK|h{C`m`d#)Sr?~Fix9-bw`N7PXhoW*~5G+We zXToz(2*&Zz9I%m(!h9$sw-x3fLCPbuy{E}CyYP|cQ54!<==VixF58&=V@D6hj^nqp+$fe}~wbR}_=g;Vvb}Iw$`)9Rf>qtAo`M)=)>}sRlYb zeeBK{dc*EQ?I{=FUI*C~%Y|&5qDI&ygBQa;#nMzfU?!bi;jguSJ6T*&1DykVmc{_NVX5hQ`2RCGY=8 zB^O(87lKG(JZqunh%ATyiQmCPabQ+|p~5_?#|XO@!NcY5U}5`dl*RvNVVO9X75lT8 z@P6YET^|0N!XqiGyas;bTv;y7+7}^9mer5#;n0qeJ8(Umn(T9a5;t``9P#%E!L4Q8 z?4M{pviPyY_#-nNub9%rlz^<9)TV?RH|Af z2L|f}W)zNYJjWD>l7-DEM&!GFyucI6+NO7 z49lks&y&mwK;QNb%k`}bYP6PWWpMCc5&bo>xsRwK8vDf#mwdVoc)HUbSf^V+xetPW z%%9R3Y`Pkljaj*EIVrmHrIljgKl+`l7(-NU5ts+cvC<6x;b4dp6-7Bwk(EQ(I*yeS zrLk5TlYlseZ4CR+N`Zb&kr!9(*>*|*{}p+Ue;b{`HS0?!#le3CJa*{b)6NAfDeix| zm+S>5!NxZ-^^=8v4`A9q!=Bbp4truPE{VYHzb+-0D~Pfvc3C(-{#9(bjBQG|)t%s< z&<)o|YGA3oMF}i2tdFp&O2t+iU-udv_c~{x#dFR{G3<>$xPx2x{J@7bL+eRvuXqjn z2OBab2FDoO7#+to+ps_+KhaQ{;568+Y7@iSBu~c)7Q+eFCd1kY+CUtjG{yK^*HG^E z(ya`Z3LO$d*=*Rm_8#Bw^!C$Mb03=KG-|9-v|y2KQml{xJShGoJU{-_Pcqu>>@ zVZBG_q1^0<;mSx^L)UjsW6u|9dysA7_DlOmo&%OXw;$v~p$oLc#iK$O7ss#+SFJoK zunGqeX8m%-Iwx0(hxXXT4b`9g;rbw_D-^Od#}h=2 zi4>$Qe#h6t(-nL@ zU2v1H(h5=%FVP=b_FEY+-3h*%O!mexp~aC_hAE-lUs)M?r4<<%108PV;G4@3#;Ria zGnMYm@_`PI8S1xhgF6#^(DsHOE3o(f_KCR36!0dOQFFsBCyP2 zqcu?4o>BN%s;?pEB^LlKoiKZ=h2%4&V)E+msLAlOFW{WDH_i#&(*aC2NAI+IB8f*E zlUA1UC`)Q+S?cOsLz<2(_$A9cnpSSezcr&|zOw&e2iv@61bx^yz_Djs|2 z>dk(@+F0q33DR*d~I@yp7y1z7fkYfZ!UK+HGbx)4CC? zRKrTBAMrZS<@x@9s~M}9>psHhFUwV zN5M%gaZ;#D3GE4WDcF&~Zif04Z{UI9DjMojP`C$1rpjrZiZ`%AUO3dL$O6cl5E%$`_6J1NvXVEZ$+Kh!~B*=GAgT?B5Q87GJOh~O>u|Cx3R>p5Iep{5tyv1@j) zI^o{WaPLs-!@cv^ul^skK9v8kofK+*xDmh}mqYCjE6@fx9BP0lk9^Ug7Knnjf~tF{ z31UwiyBTVP-oQmeJsxU=I32*yy`fgr z)vbkKWEs9vrvYBNn&N&BsR;YMK6aCH)e5uQ)7S7q2U%C_k1-{@Ln~EDU^D>M>pg_C z_!_&KSSV^}qAlWm(?oiaftK?5tSz*@Xay*1K@)?ZS0(tg6Om0=kXB#^ti?HomS}_} z>rN8HFLq*b*n`eUV#)zX%UX8A_rEXzi~(22GjX=1j~}sJ{A8#&UOJ?TTRWJ_U$aRb zRu)P7$*e43?8laYd#C~WcQ5}^q8MjUKNj1~XZ1!pM^Lin=bkD}@$#s1u?PAMrBO zrW`K1$O#4@kU1fpd=-2*5=v0$*$p6ClMJM$T5Mf{+oZU{ZrEU|EZ)C zP|{C>B@JgUAzgz>Qa6NDUPPqTEZ{7j+9f4*mNNtppjG4oC)ADea{CoLa^P;U{w!}CS^=~KJ=B;0n+ zk|nd&A%XUEL;mR^-|Vjb4pUp@5B!xQ{IzW%DH56MXlAisCwS~*RW%4Zlmr@P5AclT zkPDq^qPDU!5G7wK>Wh(3PWoNUHU(5#F#qQ_S!82VOrhT)#8(<5>@pE&G0UUjWCc$1 zMC9G32M*wC9}tO4sI7`nT_LplLHrAB&CZAV;#Gj_6Jb)#f(Sulu+4q}S_~w0C?&V> zkIz%~$lrcGPP%znWw}9M+YiaRvmq{Ao=>~xqaMGZRaK^eks#$+MXf}P*@0ctxqmby z7X_#)0GxF5v3xKr5@k_Ew~?ta(uSHXhPUv06gLAr@I&3!{J@f2h?yE;vuM8VVDPvA z4r0564gMTMTT%`&_B+8(96r!c&VXNYe_-x#)k(DHDoFA|IBJhQewk`j3yfE2D@Ysn z8ktmip%pA6ycenPWW2Rj(ELCq6)pCYbsR2{Sfnr95;Y_Syatf-w^+OhuC|ZX(I^kX z34uRoran2U%tSy57NYYTMk{?AG!UbJF#=s&`rju^eg`)*f^NTqKW(Nevhp$tUbc_- zy2ht?#4-Lmx5ZyRUR%R>a9t>DAKb8OFjcHca7-!nG-`hfBJT!)zsiC$Az5Fff%>C1 zO=h0}upiZ~;#Kg2JkZKj;NA|rgtYK$(IOU)fJ8bP!fxoIAO@Vy^cuO+70!|I*5t+u zDp7};c%Wmo7Wxha!SS{S@=A6s4fe80Coo3rf!df|i@M_F@8Kum*os5?gW-Cq!wcG1 z6#$HCUmv~pY=yKFFtn*X%JgKH+5?X*GGEr&M-e7@Sa|duP6?h*H zW)&Y5=UshmsL^We#ffcqO@y-h@d}jU*`sct1W>$*NBZMEK*wgGbdi{djO97B_%G@= zsD_FFP|YAidy7~|a>rvi6526qO4KD zkF-fc_8K_<;=oxP2j-$_R0&jCNxTyLj8fF`on&D+k6V?XYp7PYHRv<}tN%#lg8ijX z!nHSGM@WdIH6Y$-E5c2F-Kh&hA2p4d-$i$j9*i*fRm&It&i*U{x<&(`=C_g>f^(&Q4NaT5ZybYeLE{iq7 z&gf(xKxz8YeGg4Sj4T7lrd+BG*aS@ZZiw!$eSp7C!*c~P?khOB@+1#BN1;1ey#cKE1; z#(sq!JFstvD$Z_Fm!~(+^Hk*&CdPHXr&5xlwFRFi%J~Muy~Uep;tt*7PuWD1{b0R- zSHC~Pk)!x+4`4Hi$UlP|-}aC=6oDLkE37nBkEdcY691M0EmMrfMN(`oa`RpnO}Lq2 zK&18%;Mzpm8%b?721g?Tled63_re6(vc)9mp~WKci53V*bou!`m}m#^)-(|h!j+AP ziF-^|oBNdC`+oOD)5ywDxKgL$DYJno!I3)yG%pR&|zMLzn8pExH5WlpUd-R759}*j8xSF=Vwa z1T?4&Lw}1_YpLvfpuZxf(`7b7V>fIh@*oJE&FKsWY+W`sf{oK?gP(F-l1`U;YO-H( zex={6quS!tkaohbMXpMG72NmZ;O;o(MGODMJ4_WIdFSz>F*a-VUi)vIJ63kZ>V}uD z4rV(bYWI}QR+|>@hiPrh7p0lmowjn5xno zYZ5&A4OpL`6(ZIB=oGupl2rb=xc{F|Y^H+e4;R8M+6Qj|LjKAIFgoIhYG;eUoHozenfdO)*v2+jGo z319FwzSmCqvGJ8t46orjxI>c!920x3<{*$a>VNW2Qa zS-Lv0AQE=UvTLGaZub#PhXMGq+B!?bgxv0+^08DV{$rb$oa$()*%HuFD-Ob*RIf9g z^@4&tpgHUhDUc3%PpdnGxnsQO4L?D*(KY~6EWd7bMt1aYwkS{wRnP+3YYvar!ReD7 zqgWiJyR+^5A+z?uJ6$N9-@N{Tr7Y@u+@SbP{6W=`irUYt`l&vGbmm?NOG2c;FFMktDd8|yoVmn-I)>)m^ zz?A0@W~wc?w-V1CZBp^J1%BbVJoTGS>e0lvflpLY7R~v}b9{R@Ud#s+@V6TBuoNB% z($s2{;DLfmH@eHZfX6X{Z!Ze#=u4ij*lo(v$h7invuDnp9Z6Y^KD2NjD+cY&4SEjny|x81H5?K483?aufzekB`V~D(QH(il{?vKX=DFxN z)j4{P6wi^v9U6hHHi23zl)f1ML1pzo~Q269a|y0cKEwr54}T_-fFf2;19zBeI=CbWCd<(*mo@t z-SfV+lOXB`!AV}FBD(raNYz?I+1COa4C)v%$5JK_C{m*^TTP^iz{*DPHAtCUCK8<@ zzG9jKnokgtw}f8#mxwFmX9bXvKT@PK>mgPPM}dkFUFz5=NS&U|XXV8u=Sc2P_|@SV z`Ps^+Q^e(WHz^NL+52OwH%g4>Q5AE44GL>yV#NT`qFtWp4J}+5>K3H|GSx0m^xAE~ zt)U~~GB?z9lf-`@mH8{54V?iZHi_xgh{qr0`4Gm9!Q4#MfZsNamHL zl(z@a@@n`#gXIY?lMg%iB%gw98ZX?Alq4U?&3VkTan zecs7|+k>__8Th>6YZ+eg~yVHp0hRF!>*z_Upqi$>G@NIIz!oso-3>P zdJSOw)|lH`%>tu&7M-sgt%N%X!(e2O^GVtUoQ2d@$u zgi?0WTz5{ey-UFeIh+8mT!pej8IkXChQk#(gLX)_z|YW;YvSgmY|x;ojorM21$E@e zhF)IK*080H8#$+SV8PI!;8FyusKV;HrS!ocpqsH-JV!td$Xf{5^NQ`bQ3YD7R6Pol zjT|p&U@|4-gp$escu1ffeTkRQ!5t_=?F}^&312`Y;(dL#{?*Y}COjZ+rEs-e@UYd3 z3=foJpHXM8b05D@zBof^rbB3Cv6@Vk_fX_5`h?ebmjmxEM?P5p^(ErAW1ufW%+L^_ zB`Q_iO~wuS1j+R8;6%w$fDiO@6jQ`dV>>?@JCCvU^VgUJ)`vYb)@Okbauf}A!?{;x zL6GH&OZ>y5Ea( zdN7M9@)C&ri1L6JwLp6fQ)$>gNnc6fL({q*FbOXGRiz3y^0a0o1j8?#$UMeJtZ%I78OZ^GFDOHUq=O2@x;i z;X?XFv^V=fp%3f^mk= zP=CTbBcuHJm~y!w@b(G_YGj{dbW z7`rRo@CK?Gt~D*ZusAy_kU|!P0#$Mao9{{^Qx2W48>nI;a|AlVDqL8{l$HEj*?cPk zAhZ-4Yz__q+6h>o(Q9rr+412bMul`H_X!NAj$18 ztLHnUv2J`RT@#gs2OZf)(c9!c8`7;`kAz865iT*%g__6cvjRRuq|1U?27Z*bQMACr zKDNv1PT}P$%~}c<&8Jk)1LFB~7>Onzvd%N^$0$d`A$D|#%Hva3rDZ+fQzK@{@d4%A9=EG-{^h^q*uKn7|SsN0OT>0 zHk>+<`psbg%)A=HDMj!!n-5=_w`7k=bG1o5x=-up;aTgRqgoI{`bnepd<1x3*YWT1 zRtNF*$tFh0o`9vrT%vqny-9v>g|d1`c~ldlHP4qE9m`eZo|j^yA_{8!E(WsNbnVQk z1*s>@@p!~Z-P=uru~Q`aHvsjGu#s^8AQhwzztN!n#8HjR`5>lF*|=xUUJph85hL?( zh#Oi9ny5Q7*vA%#H^DP_!TM_3KDF$%i8|?)7Gq?s!FM!^*8UsGhqEhugLx zuXKvEWZ%te2QmGKr^1EHzcIt_8fu4cf}xith}}f-{<6B(h7QSacrJ6nJNwWFZ2njk z>Gx+86dI|5aCCvkc$ec+@C4Zg0@}EcUy9`K=_vma%pqPnf#}XFL|%9a>WW8QXbsXt zZ{*U*6h4jW-e_2eXRg|T4}C0vs!a;cNgz)#PXKEynZGjd&>! zuaN5(%KY%;uQUr4^wuS~gEG)JkpM*&2Mjf3m&xYA}1oZYUn> zCp{2wexfPty=92^G(|PE3t`x_1)E-D(`#%BcgoXecDO!y(WYtJJWp=z|NW?s57hX8 zQ#=zjzH~Vy^`uG3+N2>}Cnb5Be*XOxRPkl}8)u3Z-?L%YlAcx_7JS_a{lWtyY#jUr zxxKs`&|QFL==_Q*au~eJa4(kFAw&ffx{SUHv(@$ZD*xqP!)_3PKFuj(UB?``2x<^8 z9Ez@wZ#W+K6ibrQsqB-$`h0wx(xd9A)}14fdYri=!e-!ee?S$JP5+M3M|$3frXD$1 zcKauA+Npe`!9UVBR9f0D`Eyqf|HM9>JYB}F+q`+j?oGe?!q-(pmb4V%bE?xGt2*@e zbOVVZlbzZ08(a1cBG)nS(6g#gfO-1PjTz1Z4C$k3KZfGp(BmW4 z!AsaDf)yZtRfKs8@Ny@q=_He1P~ip{$mg(V`aAT+SrsuO%1_M1052=w3Ubg-I+&qO zKuw6z#9%giCtnbWTUwt)fL|;YmR483ADr2cyES3=b)^HaBx68DswC*N;~bJK$ND(> zsA09>$lr#Voer(N%s5lNg{zCeJUNy?cG?A_`BPTN@1ufRGQCD7)gk`$nv1pOH=H_1 zB`*$699IDy(~f70cY{nBUa{?%$kYe9r^ag}f$yh_SqP|?b&zeKL8sxpZ%3OVsk%ld zFm;)PiO$!6X;$K+rofF5W5LfCtJBx;M1dZ@EX?3du<_&hRlk{x^pV`~=yz-H!kl>p z{l%dgP^HNmq`Ou+5C2I?`s`K}o<0RLp5pK@$TE$Z0vAvv$>#9{BhpNM(8sIGgsCvh zGX^J^aDl94D~f>UsymGK8a}F_QT?9)lS&}Z-WmlLqfZFEOluBaVmO@&)-qY$M8_i1 zbP!*O38;1@SXZPw>(@80|M`b4Q>GxJe1^8DQN@K|@M+Wl9=(CdJ_CKnd=TE!(!r%IrV!oJXG7epl+^BqmAjvv>~#>#u%{~YNgzSC7kdJY)Ok~t zr$UO1Ci;HXXw74|2mHvO%8IE*dsAw&1>h(c%ioGPa1J~biDq++J4g{dO@Aer!VhuG z@W6Ps4k@LOtp@JpWLYo&gFXv(d^1^Yjk>&T$g^Q!j^OUf93}6LB=Rl*_*0guya2Z6 zat%*qQVJO0U%E$R;V|>bwDAUAZXi_b6aL>uOU!O-lIkyiJrt|3bFl4k3($>W1 zMP#$Z5f_!c@Ce_cSR^G&Cu?yL>EW3XisUm8>&+o!y^tn#gVPYP-dDEY(B|p9TqA{Y zHY#$RxA3$rGjV(m+;y5VXT^6jmw66vAKCr~A75Hj0rx%$(hqW7AcL{5Lhmtmwg=M& zr;ox%J$TuK8hZBco;!byZwf6?+N$-sZ@rh~6Mz$ExxP*b7v)deE*;s1h28vDOyYrp z11P3vPne7CoV!@M=LK%+l3efXlGtnDT7 zwNjz^a1qj4@c_9a{du{D^jX%>nWR&+O5a;PuoS7hwD65?IO;er$AatO{JA|#7asSToor#d(ZV2Q`O$@}Pcr;=fd1**qq|!yu4kPS=HT^$W zSjnKU$RBhzRYr7V5{DhL>qd2mfEKhUf`hkvc=zto?CJ_Fw#OLu*Uv7_-JmrG)5n+^mv&t<>*njeJdY~?V7sRdu^mEeNUrw-sm73n_58A7x6(S#`h0eGlNV?gRfxBe zyVVc75xs1sfqp0vnAcOVq@w?%G7sS#fAuFru0|3Vk(0FWyU*NgDp88Go1le z{s>I5Sr#@BDsbP=VPCsIay>>=ufvb=Q(}^9lqxD1VP}JH1RdgE;Nq8bzvWpVpU&&5 ze4QAgAodVG<_GVX6YgZ1qhoTM0};TP{d^Wk+h_KJ^Pnl8=@CV({5nnf4sf!DGy_Tk z2x~UvQ=sqA*v^jdArd>@!GxKtf<&aK@*)o}a+x>rh)#R}x{Ah9w!DxZ&cCOuvBY0h zd`nU9eAcfTmA<1GRz13^1x8zYbTrJYxcNTcK8?P;fDY)p`)3Ap+-n9ZYu#Ek99-TL zZq<{*!Jsd6S|gNNO!R~|v9M5CNg9C3V0u6ne7yZ_;9}C#01vhnh7;_8kM&FjgXpYmj#`JRSyPJ1G(YR=_hp`ilQ5-EC($l!_5#X&VnFx#3Xus6lJk3MgdR z)Sj&aZ##_udigXmPr&OO@z~@Mzfs=0kf#KEVm#nis?VOlcLqSp-d>7yM)9J#Us8MX zUmL>n zelnj|1TNR47SQyLHI&_;|JT7Tqrp8i%*Wqod?e412UIcz^P|APly3r~;wC&sZaDDy z$5C%+q)Bfu14Cz=j&ho`K*IrJczfZIXA2YTV{jkADl2e~BB4+j`3L2zLp40uHWGpj zP+@oj$vt>n+VNjZ`kVpVum`@?@*LQPj{Ku&-r4VKMU7TzFHdZ}W5OSs2;{>h&v8mn zoeD_m2DBlZ!rw_`!+0PY>h!87kqxNLIV9P1O==`9WgfH9%VyhEWvDZN@vgle#V_gM zIm_dH`|QU2I<^W83Ih0wKm*3h&zK!d%GvKzp1K6|`IQf!u`$09yh03k+If)}yp&{9 z4xzBibfnV}fBaNxZ}tUnq&(jbA71?;AOwFk z!;81v%^I(Zs(9&Z=mkdLwFu{~(a1UlP}cXXeB=g=;mtzT+k%+%EPh$c*7*Ab_>wn> z@{xd9js}-y{O@o=OpWZNaeoZvw595pAof`T0iUT45h5#j-W8ar@sj6aFq!bf*IAa$ zqC;M%gcPlNixI?tQtn8VLD0+V{J@Ub1dd zv=2PLP)Rpj_o4C&0{dxTK(5cr_QH6R5Ghh;fZSs%F%M>)#Q<6Ca1)rYTbNXap+ytJ zM0P7ng{7X*i)z%TvM|V0!|E9Udqcg;Td#&rOl%~eP3gcLP@k8QzTa<6*wOd+UmE`w zpAq9yJ@gK(z*QQ%no8fL*63+VS>e`31Pu_jX~2^Zv~7!SxGf^@NxT6jXiYD;k+JXk z2-)9!nKqkutcZ!US{$4V<-i%DmeWC<+X=$opJVP1mjSYyr3ir3=IRJM8`d%DEe4Iw z&iLsBYoH}{4jN|7AR_)3<^V-`Hay2Wr4nVX3;$A)NNTAeb&Ehg5ZFVt^19^`aHrd8 zzs?%4#;k#8jN-Ku4OkPbH$ic+EE;3IF?zIitf6Qk@L#d_vsQYs^Kn2Exz1mF;y0fq z*S^eM9-gUo%_1<^i$@eoK7+l8hMm3mzWvfqX2Gi;Z6qWj1={fIpj2}1!rkMlqfn;e$ zhh6+Lq)3*k3{?ach5mUr%jx@GW6d$=Kb7X)y&}57yX&9n_eWOHoI3CU{mP1?rnKuq zuAW?v? z1y1RP@w?&+HnE%6DeM%+1p>95!q}K;+I+9!{?t%G|2hzE5jIc_Oe>FkuI#MA2*BaK zw0=Tn0?%4eGEyMXldjgn&Sl8LLlyu>cx^Iv2czY77{Z(*+<^zdGlE@)p{~Hyl~uu` ztQqpMiE;y2Q-Y@*R7N8@onJUlFGG85D*3-V@ocZk$BdGXDdR7*1p0>sF?g0c; zj{1J$B^CPI{*AA6Cabl3Cc=Z#yyL=UUEme8DTkq*&@t9E2TgBx^mKCqMaAkIL7-J4 z+i`kTU$BmqUcH%&AYazB$xyDbe<&LNp3GYBDWT_Y7cdWS}d1SNA@TOn7<-cFt zgm*M7mz&kHIvC1x9h?ev(8)(3TpKTGwblES?FvwB;%JgAF{hc7tGysFy+^#;p zA9Ia$F`zJQ2=y=)>CDx~YcSP5ML#i!k3;x~V$Ds+tF_uV4a6LsGM+gG#;lBI8u5%K zbSg1$=0uRAZLX4^*J*5?Yg}?mE)Sp}If0P{DG&Dl`|&|=MF>}+XwofDxx5V0e+YQ~ z|GA>9igE!ShZ^S_v;oWH$igyJj@`;#+{w*psA7k_oejK^?V3WBk%+Rd5Y)11iXDdUcu zo^Z;8!M`0$kvnjT!XaDg(LE}$kWyqi{M(aBuPUn~56rs!FsU8@j zO>CPu3G<0eop(}xpLJXde{HC(?Q43?F|8(aoZ@1GHF^Z*rdE6}tgVi)<}a9Vjpsmg zWedwy%jAfhO-Hv6z!CO-0;kIela=DKLvX|ZWax8ZGtKP%kh_#;Y1v-0w>I$cJm>C=OQpsogDSYh@Qzs*eE;09w z<~Q~rxYiRLBwVWl(%#fnFtWoI{DgWxyZqJC{U*)PruA<(vAw5M?QK*TPe$KpK*59! zwV&%OFCYB|(5|8|)y}hn3SePCKQ;rHb$ni4hvny^VdYGgyCJfUgU4gCuv3xbw{utm zE6B&cL?$v0M#$Ts_NU#>1Vi23(9N>3Z*sext%mET1+d}TB6U;SO&L*nT8oW_t2nSx z#drrFwV$ZmI&%8FInf;$L`x8ss}ApXx|5e4Jg9pyPgI?_lBT6i(@UL>fB*jMFLGlp zLaG<97bMq8zy0lQ&?;279&??2<3X}VO$=zw*ZO$eAzEcanH4x6Flb zU83xeWj)jX#K1$Y;X6lc-59xb(`vlvIC!}D$B8^L;oHPD&3!96e%-ZKWVhsgU1L4r zfM;u2bUFe*Q!;=*&P)VrZz}+VRF{r&>zzbNJAjTbSYgo+am7a2*rElTNmjkks>meQ zaD=`3pCjy5*tQU`!%QDGIZkMcv1kokY6?X(KB{P%) zru+E0AqqbU^kg3Tk<~@tIhb|;_1Xr`u|KAmT$#?Zc5B#e3mwQ|HK*kUOuyx@R%?sH z@cqzJNhyYU0SO7XrpEK7N~&RZqMekqR*l||=w4A;=3yGVko(wFHkGfVT`1n-T)c2} zOW$y7AfIs>in5aB1bHq#%z{=U@RS4g$S(*>ob|(bF@RQi;1M=}@H-A-&ku}kKn@L4YKLMdslxObS2KFWE69(;i?Zq{69i>_xx&zdAS8(B1(?o5)Qgjs8 zc$^vjnohH}I^n4fz`7kkgWPUyuF!Raq+3R^2)>iLh%%xq41fXru}Sx#iVVP4Cd{*^!^iP4dJ-F-s)gg_fMvt% zKZ^!WtB=yq`+y8(aP4fkrc7>Fl(6h;iVc!0ex_!%L3cp3iM7MXRvYNJ$lpz%G{aB8 z;Z``3cJnWg$ox)ZLaetjKWNIJL4H4_dqkCyvGQAaQ0>j(lX(--hkxXgxWB#vSbFWa z#$x$>8Y(`9QCX;SJmw6)!sUec*%d=wqg{kJnX#1$NXL0`IONy(3|0XhCaCy9H3rX0 z!yggFD{=J(75fpucW3~N2i~~`e;&yP{>6P%u9C?s5C-j!3E&0EB_h?-g=ENa$10{|UF2m$rM;C`q z4u6*+p(Q=6b7ZKoUVI3h6w<2y6rrS|rd0qDqWs$XqWsfiyu1fS#ZZ1y^z!x7L~A%0 zu3_W^u;dK6pP7q!G^FQH1G>N++5~TB*HieHQmDYo>O9kN(?N(Pyg?y^oIO0XGz=0D zFsMBv_?FUu$ffe)g>O=L1Jf$3QKsWBugcGy@)S1XE?GGHZ&VOO%-i7F0J?lAWL2KM zK6e!{M^kBqf31#?mU(7C5zp}I|7A(b(kI8>Ju#j9)~EmXN(U5IZp)|Fb9|t)FjabZ z6K~`oy@;gGHCBX=fq$SA{~IR2Wd2~HDJ9=y=VR6%PK`XY^&H|^gM7%d<5MAe4K6_0 z02|s(@URekdU27)|fifJG7YpeyNT+ydHWsNq zDvuUpl{|*JSnghc-3#PyMAq5(wA~YM|F;P2YZu``LR-z=rA>(;edo*>Y$DJ3U=gBJcI-MDHJ{QKoQ>6E~2t^VOZ*fB^h3r#Ytas zY;hwhEpjQy4&cE!L$t~_IX=0LW9%>o(pHr^znqM*-Nc|!%C)K6@fn9jeZFXT2V=omUX3ee4cbb>?wZ4=v!iNmk_H@w;y7KI4< z{|Yh{(e?|`Mt2&~3qD+IwoBt>L%r~LIo)Zc(nX2hS*jwzr!?For4?GLa=FcFB|{H4 zf-uw(^n}giPcTXOUgA@oH@OfPs=%)FbUS06`f~E}gE+!_&^_z}+UKl}l!2T;M0bMM zdx=smxnaY&>C++cejbzIJa@}tYW+il2Ae8)fK|-Ni}Mcfo8g_^P5$}BWzVMdlgF?1 zRYlM?Z@HU5YmeHrecU0SdVof-_43Uc*2D|1h>$H_cn!R8oDIWwtaA70@ry_n*am|I zIH*_%tQ4S*J||9W(TgYl5(hjqWjcTH@#9)of)1l9CE-gN8dfa%d@Tx-yDmI=3|~?v z_>!u{;5&&WhW4vow^4_9ghr}9`Ea+a0IyT~amu-)yYYDoZYxCf<=6EBWmDP*bodp$ zPP2BX5(TP0|JNxcf|mKY6Fbh!Q;HH?AoU$gQwBf^270OR?hI8ZQKj7o@9!q0j$yek zAE-FF@RtTE@b&TSd>?J+hv4d&;mpuk4rHkEKHx&s_;uQdDcDAZ*U_MH1Rb)RT6nng zv7stD0ABYe7w&=K_*3IP&9CdQ*f5l<=>;NmO`>U%k(pv!<(G9|9 zNfj3YB*s5e7b4iy@9yVly8Y_|pKNauXj3tOSbapX&+h(s;*i?=T%I2@c^XL*#Nl~$2}KN|HCp|;cdoZj>M%m=?tYjxQ1 z$;VDd(rGEk_SrKy2%7LuVR4$&c#?8t*Y`JWZ0b@CxZQ9K-jZyk5O2-(sw5SEcX7{w zZHQG**Lg;*>)_bVbv?0NHyk*8=-@NVRn{76CrHUXx^;S|ufShQv#w2&qM?OW=NxGh zd~EV1*=#Va2Li4maBv9Wnjqoph6-7lCYYFTchlc2-gn{;&-)$qV|?S$-?uO_^+xKA zlT6K3-p!8FoBFr4zu>vJb=TpeU3b-r>(ZeSAQ{g7;C%-pccBy-iVta1Ba)G<1IWJz zaf^I#sgCxI(lFu4hXn9`CY221nOi+1!|z+Hk(s>z5oPvJ)_u|3l!ryOj^+v?d$1WK(|E`@+zhhebA3EPO`O=WV`UH5+P=w9EVnhI61 zFY%Vbr|xZAcWr48GIm@D{EhI9*1&+21);4b=1&x(=WdQCkPqNJ^yJNzLp|$%Yx1>P z{XQ+aS3|rf(Qt3Yn+sf`=4`FHfAy~QmpxB6G(b&+YEMg+062Kw=e$g*I=!2>1L~J2 zBH2qH{8ZsZ@v>X8HR^%YyVqaw+}+xeXYh$;*wjp0<6kv>rFmr4?hQ9Q_co{PXw8}+ zw3qKGq|Mc;ZK||4?{}=cC6A{-&2IG|I4MpVnR@A8j_o&Rn5TUGFB*384Z9C4F7DBS z%~ubt-uvw(&-(8tjaiAWnAOMx_j)8Hz;I81NmdM#*o-PKn|vagig|wCI)Cmm9~IH2 zP3Sjcu&3>?jb{Pkp-9w{Z4!?$uI{+A+{Zw(-A$!$oY@M+Ob>YLvKX2ALbn^~ z?ns^+FCxIgBsIRqL*IS8T!wso1Pxt_gE-a@t~0>JaZRig6B65^i$5Pz8^kV0R|@cA5>BmhzKkkj<3OcJOf!|+v*Bk+_V zq}ak*tCu_;i%4$zQy2gEBk=;Rkgj2^7zqfY2t)_@#uPYg$Fg)D)&Vn>r>qLD%c`L6 zJ{4W~Exs1h*S}%1A604i0^kBNPz4X|ID^U6m$(t16AuvTRt!WLFz3lK!KAxS^!LXv z{E_T?(BKmEPU^?SB%3iOYf%PR<8f>#90d2}*ST+fq#}D^x|Puhz+yiM6R|z}l_fj7 z?Kjqsb{Rz!Q=Od76ez#LeMiDlBCKY1i?4a9^;}(FIOlUXF0$f%^Z*btrXG(O`o-*d zFq{3__&w(*?D0UBE(YlBWnhxc4L1Aqbj#O?JYA^XP`tTXG&Ki5B#~C-6xLHBt%_ho z-PH_8tF}R;)ptNz1;R8AfqA$N<3J>3&)2Oy;9$!QN~@J3yU6Y25%=h<_?R`RWr~Az zk{#4oqlZzz?25&pU;su=yxVDqsfB5DHBd^Ud(RR;~%UIF8i$ z{Tlss8l%erRyLCL=ik#sQB%|eO8cVd53r!f4;0Q$x>>~qZj^C4Y?sgAwAh88&oFo0 zjvtxIwHtY)yOPlurt|GE?@C+CCCp<^IbgfG4fnBiG()UYssFME@Mp~QunHhKt00qk zhw!VSf)Buyia=@JVx3L73$}0RXY%}1I2TxFjD1&GAz3d90fl>2<|mhjSFb#v@(+-6 z-FmrCe9LFBZ&luTSaHnSr+a`=%j-=c%}c^w0aU0!>JDtF;xfSi2v_j!R}XAZ2x$;i z?WIa!soa=O6hh;RqrAYNh-yF47Ti6~!I2N{h;4#UMUc8R4B{f;4y?D0m9+>ye%dT8PViKfF4DrE6`qfQm zp1;~uuR4Y(y5U7WL0x!1;8Qe_{1owD?93S4>Ett*&@XJh@a)y*_*k$zsE7;$YXR?U z?X>fq{LIkLZ@&2K)h2mFNr;PMikcn>uv$n~kqVlI`Xb`e)?c1K-zpC%E-wK&5bUmp zON2p;z`-E`*Esj``PO=I!9xlL?^2uBdjta5>wzQsrLCu*yxLShREE&ry1X{_vStxD z^ZBdI@=OGr3L-2YEZ-yPbl$|S>&ur&*tZz+Oo5vd@l3Exlc9;uRC(Aa1_u4b7?z5KRfJIRxBc26zJf$GsgkC84=Xj08M=+sj;y}4P?58c}Ohc|LXZmj~>^(D(7tRu|-V0 z6$5)P)1yqkTzvEd$&i`35+7Gsnje#bx!;RaaIGTu+l`)_Kh#T}uPy-yzg&HgY(DnK zBmG+f5ULmVIRRX%r2lLUi9i5A;qkND7ty>j)vZws=3w9iZ=|2+>6CScPOm)WS@+G9 zLCL=Hy(V-UYt|oDzJGEXf6F_gPL6YJoV#YoQ#%D@DM?NXT;|W<)>|s7%vg~4js^3B3XCXTVOa=w zppt)#2`4H8F7PmT=y(Vu?W68h5$sK;lZ@%81HK{E$=$|v-Y z7TfO%JWtml*YuluWc9wa7d`8~n>uEV6xh~)M^6eI(ek&9!Pu6!#UHPb?tvnNWK&kb zTjM)S%2fpRy1;D(|2M`8$f)@EY*n5T#iN@4@yZMwp?aLJ!TR_Zm7Rl+b$(y@nEc8SReN`JP!rl6&v_zW2WW z@BjNg#2IO|T3H&6X6BspoaeCE1Elhx=IPB!COitYwy8h3eGLUl^+&X>D z^E9AiyVt(QtjJMayezXa$ERWxFg8Rh&1kpsoaLugtAEGggGKEgUPkvdtc8}7X8pyD2oyIi*^rg-rOrH#Q zui#ztLqJqrc8B#X$7(iJPH>y?gi{DyFRE6sV17^=XC4O{xOn)0yRpHvdKn7h5JdDz zgXF4NUAk7cR&Vp;C51Ht>=&jRZ`$zrrJQ}c9c>O=?)!x2Z7=kQCUQ2Ls#!g{RaNSG~8vf4}%=!q`d!jq50GDP@5aY(dgE&Ds4*UT0 z9fV4qbkGgR#WjA)gz=UJfGyQ7jd_G`(#?->F#2R3xkr7i&yV%*z4ptW_;JLpK|9l# zwMO9Kcv|}Yu6CQ7W`7m*@BbX~FqZ;M-$5g9HNU|sm9BlH%Jxc2gZ|S$?!FIBG7hw> z@jLwnOk0R@868Vbq}166BTyr#I^~gP1&_NgRKZHL(fu=Pe%j|~0&Hti3sx|p>Z@5QuYch-1)6_nc7fh{w<%O#h|&_jy|J4*Z22nxz@qXNiEabq#<0|ec2}V;C}PM z7Y@Kd&7ZcV*I4X;0i2c{!F=9xOlvP( zlZ~}R8$v`*%Ph2L8|@37zJ<-WPi8H0Z=ow5E8f13>;l&bfD-I7;x@7o^~>Od>cqyj zfK?0>yD$Ji{cqvA1Uo-m0tGm+P82+n$5kM=-8aE6DYI|YcZ7{~ABQ?VT?D7rbi${8 zt<)(*n?hSTkF#+Y;!|us1?0hxF$#EW0~@~B2DW=l0BvAlNP!6;@(C*!5#P0bYjiA# zAYP3t#d%w(%S1ImL)>l(&X%IBz@G-vWxo7kd;fUlYpLIqD%t%UwDlZu@bU?#Hdszb zchWDH+~~NmW5=1yTLxMpi}^m|6c{SMNV*%$H9gvEkvpCEe{-}g!cUZpPvVZ%k3(q!pX*}COlaPHI z9Xr}kZI-_@CYqsCMsmO24jPoYF}>dUJBiVUF`t{(JU$U2EWGrzkpiyLI`#v*z zSYhHdmjqLp`v#s4h#3mVS^*K@z21Zt)}8_>e!XgQS6Gn{ zi-AF@6TG-lbfU6SLQ-uz-k{N}O!4^ItsO5S$nm|$SKdWb@~PTPn5}4|!2+8Kq*yda z?2*6jsvJI|by68?X#3+&mGALzTpqJjz{Q6y`TsG;iobc{ep2qheVYUHfA6hbfc~Zn|})kJfY0m9P=| zV*Io4!yKOR&u@PzX3yfsI5Jz7W4`FnzPI@dl1tR?)i^~Nd6}@PyjM{bF=g+Wz7KeG zz5&DUP~I^5A`?I*mh9-r%E}*pD#L92>D!x@42y5N;Zz#_oOEiZIA|l7()X;taouA|r!z3=;WqRSf7#Fl z*w7O?)wL#Y8~RVMp`TQpa-(e>E?(Ha^VqRYyJ{zNZrj+w-UT$--uC>ZJgaxDdhOda z!rOGs)6l>17`+Ga+8^pMd4Ge)?6G+BN0?{v?(4C5`?h#Oty!K2op}(^Zb%FB#p3N~ zg$M08?%X3c`mw{svp?c*x7$@Of&U%$Dxk@>wimd?J$6&s% z#ZQF6Vu4LeTy1p4YnKs~ib=v%`YW*$AO#yWuEFu=n%2bQ-YaQNS9_3IhS{oBWA8Sd-t*!!k~E|hk&f+5S9Vr9Q! zrcj|45)XGuAP^eIShf~|vm3wmiq z=P!Vq(gt56{-HQq4Y&>-MR8{urZwGuq)3U=1O^IuFuaL#nxDWWHRGn=bgD0z2XA5g1GPLI_4(98aq5dWHFc^lc^n25M)hItE0WzP zaei$p%&P-XaVyyxJn8=63{6EG2td#m6eqA)mx-nqwZXZoh&Id(EVOUgtLUJ^A76z% zMS4;LJ#%Q88Vp(aUOAIt)-o}QL`R&@wOtB(G$6(K?R|Cub4NUkm@l27;__3ls zg*Tsonvvbh-8S?((F(Y8Wo9aM1>HjAZ~WvUP9DiZ%ETilFei%BhPfJ5FtU=BjBJT( zTLojEaz;8GRDBhUxuGSIaG5r(U>p%{FKr`_WNxXuq&C=WG&d?|hkXfOIln4tNBjh~ z{tDWSQB&M;VTcd>62`0X^s!Q>5?~!bT3Me4(~Vjw^(-QME8Dfw>Lt`2HcF^SS)eP! zzXGTd8}5qE1vVcGLh|Rc3(-b*jC0Z7KJu%}rV(~+D1~V?vjlF0rsZvvMvS@WAI;C* z)3fpCnY6)LOhx;jXUhACdr^DvGnIG2Dlz*tCmY*plPXYTER9ilD~`42R!~RT#1hzu z;cb=I&Yv6Q!T%v;mbQa^w2x;4zzc*Io&}wzP5lJJv%xoaJnVH7oTAoiqFvQMWdgzw zS=$A;Uq#r*yKyos6T!ntq8neSjjZ53IvPyp*|TgPeSv#qC7j=Db7Yz(4pC#NieB7{ zCTnKQvmtOjBs1(=ti}Q}BreusnToF8izTaO)(kL!W-#j}Bj9N$8SLsi(av7jgr7Lt z7Q}<@y*6N%VU1TisypOTr{Xbm`<}V{>?v$>BT@Q#cYP;0!!S*8bHg>zd zG)CM{a2(<4GSN4EaO{+TXXyu<2}nGbyJKWl`kTrXxXr3W{8Mn!EQIf*CIw%{ zFCy&;(%N_0*Jo(Hv?{mO;}Zi?KbX*WcWN8QhQ+KV%#$5?C)k|3FY1VKL z-0-xt)*N8v!Aw;f5obwSQck9^q&>AIVvei8K7Z*U1}x!n46||eiz2Pi$3?Tt2JT6{ z*_UHQG4NAP-jP3m{T;mA)>L;-Et3=>5!WjjYoJ+3C%K*&b831 zFsFR2)c|=qn|+xzyH9Ib0Anx#xH1J4RDKAdZQ=1s-2e=IyVg~!!Zu@$a0M8!C9DQQ zH48ZCJ{%v%>RPln3`SOiv@CPU(2PTo}#7E zw({0hJF<^iAMNE|50OGPmDrzn#$a~VcFjO*mhs{s1sFP-)qpFMgn4)@?8_EDK|lzM z$Z|0wC+3|U=JJTk%;E@RG0Il9lfZso^h?K^h`*5P}45w0(op%2i# z>(pQ2_+MCy&4B{pig&lDVRu^i-+Gj6=M;rI5v(MRrjJ1IR0q$n6FUTZ_d#f1COBud zvwCx2C`KsL9QK6mp!MJY=|m5s;rs}TX8yXaRunShSAgRj0UExPFfSR!j9w6kb; zTE>b&zv!m(*i#2pT*R}dEG>p(3V4pm+cQ^+tv!2DLj_|-;A!COdVtd+j&=bS{cJ3_ zREH9)y9hOvkY-MX~9UM=G8?!3mu(MWnI?i*P!g`$fa$LiNv9Z?5IByb; zi{*#0GoGXPF*QBs8tOThjrh?@a<$*nFy$;^p=ABt%U3tVFadwH1Y8VNqa4n6Q{`$e z(xC6-dm(Gxy{py>8!IvbYZs0PtX>?CCLy54i}buB$hx~1uWZ2gtV9|MtX3TQ!h|}A zG6-{yOqJoF9R&p5!S%{EyVe;5ptsdz#k|5fGfk#z?d+g?YA2-DU7X6J7HuRqK>daO zp-So>Sqdh9cQtRoFE%-}MxwS!(`!V#t3*iJADJ{)lhkAI{@TPh^3S3g1}MP~S~(>| z($djMWocGN4-wEYZzJ&h-xt2so7d8|D%0?VVbWM zcVJ>!S#&KBXdMLfp${EtWIbRTLB1N$PR`_i%>9plY_r3yASou(G=uh<-`h)750@jn ze2BUOP1{F6yM{2Qf=Dh6bJoH1k%JG-HAO8LsI1v)D!-#Xz!A(>i-TK<#UXJu!eRdD zx(DF8A3GXymLL%`!!G}F*(Gq<>;~;31H;t@9%C=ML^&sMHgLeKdpNd=^u|>A5E!h1 z-^M+4cW`>{1X|2#m9;`!L9g(m+QF(W1%CliFDgjApq^tVR0CiWoe-qALsL3CIIerq z2Ar@0U1LT3XE~Owl~Gulqy?}7n9>)(4BCv4?jO2yn94>wo~LK>VWf4_6uZ#>Th{xZ|NastgPsz8Ga{ z(S!i?y{KNKA90VrD&RF!e?`s%qV06J4X7WRiu?h-w2q_((#>|}c3_rgMj&YuBim>r zWo24H#Gro34)abvHXRBv=;1`V4`WC{+FyA9>nz-R0SHnC(MB_Y=KHBF=4<68rf=4s z<(rJIjO??K{Q>GJQ9VmlRZwq!z6R#GhTJM5Ca*5|r*9}MQqpcx73l@fIVh&hRb=Q@ zEkNl5>X%_iB*1*|LeMOPfd~$C;J$1toYWXSwyFSLM@ecr{T9kxxWj3{%!1#*R@_)3 z9KZa=V2*DIR#pKm2Dk{^PszOdiK5ax5&TH`{_%d+ItBvWm>Vp!D3L9~T%s^NqfkEy zn3&j)bg+ZQgy4V;P$*>>`EkF)0TMNWMSr14n+an?273n;c>Ux*zZE>l3q*P)0EOSN z&-EiD!IbRaYW!=U6rak7;cc$^114rOWJ#L8>W9$E76{DQ4YXx4f>&J@Zc^gtHiVzn zl?JsN(+puFMnG?kAf-O>!)wPPwvo^J0X;Bp?Zyfs#^v+3J(+Nu)tK)9rPzJq-XJ1F z#~q?2&^w~~k=>Cakf#IOyyo1~9-~!r&)@ikDk5vO43}1jg8%(1%(f(uKd}U{(u8*H z%Ui=*o%t{t)K`f89V@<>x9{wG>y1M_624Pt0$>!ZH0;cvJFqZ+_H`cz3xS(h91I`| zR~9fmYGzXbzXu*B%tnqmSJ_;#)25M0^o~TsZRDsk$L6XsV>-#8cO}{!zai|d$ii%{ z821_*IqCTn4l@QZSG0+;Gg_w8f-!9$ttnQ`4m`|g=-N7N+M&oI?M-9oIV&1$VZ3Yt ze2X?`GtlhUkhKq*2^RJTnAmO;X~T0+*mc10Aazb#L9I=*fq!WTsxN$ZKLJs=jkQl! zb1j=Rn#x9-xJIBc-_kw6RqeYlv(pw zoWcrx&nnDkm9h=ysy68gon-{e64tYgrTFR!>_N@awEQw!^d4Y@IIcI7d%MFZW2qQ3 z%+6yhP>u`*m0eM-oRu}hK!Q#r72yM+in%9{Sh7(Y_y6Xyii5LZmNC~0tJysGHU$^; z-lcFJt~YftoJ1*f0_FdMTQ7WO?RZ$GQ|)kmm{ zpDD0@H=s8apv^5*|9TM|d*2}e2hVDg4|JG_F|5ueSXfj&3>R-MBXt4N;w7a~;zF@JYm|Cephu9~z+r5yUAvvtSy)6j0Re+F*<0Z^RNb_WO6VwcyXCy3wnR-F!=O`#1=VL_F$Y3K_sFg*(#NB}pQpvZ zWb__UNlO)EP_lthsFM~LjxHGd&ecGwD#OzPxGfl}gF#K#3<@+pBMK)B2Et~n3FutI zfr)R1XNk`M6Lx!uY$rDIIv5s;&vqEh111E};XE0~$1BKcdRDsy0Lpc7>U44#x*WY| zS4&{BS&fb`sukRgk;P*$PyyAnXbT%IIsb4zl-U@y6;Y8E+a~l?heX&X8qrw#iY?0a zg;ExU!*J95{2#6$`t;mir~g^R{0FBmj?-7?r!VJjNOGGYl#nxrjZssG3Ir_(&Cdp) zW7D2PStIPQs6Dp1;FZ1&T$l=Iu0;!=*}&rlg3(QVFR!PT^-w(`Gy`Fuz^byyCKU<5 zh>xF(Xd8!-U_Q49(JHx$;4%y2EP&n@CY<9ljGwI}&NdNet0-!{fp!F2eAG5tVhg<5 zCABEn1h!MX8YRzSCz8NfSS8qr9)6SGr#3rSsJl)H!s;b!BvMxll3$7bM;H~>+~1( z_w>K%f7id)tNL7Ti+4xw#ol|ok9eQ*zT$mfC?r^g+CsW8Ko};>6IKfALGf~j7)Uf} zOxlv3WFQ$qCXh^WgghnhiAr)sQS=d)OX1R3X@d03(9^KZC&;IbPkW!yKHGdA8VeXL z#t>tKv8=JYvAwaYv8QpYak+83akue^@r?0`@wV}a@ukV%G}rXR^xE{!w}@|)Zw=o* zz9W3U_dV-Z$S=sx=2y(Gsb4FTjICZ@08yoKeyS>oZlR7u3)ZdPBwQm z_c0GKPcY9gFEp<-Z#M5T|71RyZ&toJ`IhC&@elV;^#9s_r2iQIAN+sxfA86x7FbzeSAo+7%N0y4xVGRg1uqsVQ>cBRU2;LWs60lVBxlL@<=g=80H1*9fN=p= z3KuDyUU*;Op9)|J*wo81;_Yi`T5t+O4oowViJ1$&@fu{W}Jvd^^tY=7ra z91R^K9P1qW9nX|PN{|w&)K|VyMk;fZh00;&yh4NZ!Nr1;f|G;02lom7F8GJw1HspV zp9Fsh(T9YE6c0%Z=@l|5WL8K{$hMHfA@@W62=xyQ40VK73QY)Y8#*|2Z0MrU9igW~ zZ-ssc^9}P43koX}RwJxgSV~ymuu)-IVROQkg{=H=nst(}vnsLT`!v>?RB$vZ*~L1Uk4Spc0yRs+1O>-Kgc9ecNbq zzK161Fg~zr{Xk-PT5aXf5?qxR8%+V-ap=GV6m^h~lkVXsI3$|XwJYqR3B$@W^hzbG zzz+11RKAu5ml}po9>%>%`&wqD)UQFh-3JDAtsbEPP-!}Xwu()a*b&5~3$*w1OA8n7 z=R@s;0j z3K^gFsRc@jhRxGh$oN^TL!kL@zJ_=AZ@zq4;Z}H9Ol+}Y_o}>COu)_I8Pd{5ldFky zr~H=d8y&(B=MR^ne-`z9LQ{}9ocbY@L40m#JtVHi%V`t?FKBaxw!nj* zMC&8y4aCzT+69t3n&jq2lQxd-a}L3d0BW(D>eyY?Xc083`}9Y!zp$w%jJW)XHq!0i zWu!LIwme3pTNgCcHh4%lUy9vC8zdUda3K2g?J;GIcnb79nzUHoMuy76?eIOU){+j{ zVbyH_KUc;MeLBmY=(I>0Z@~bLg6B)qpv6Q%7S^D}MUftObIxzhVWeG6^B0AplKL|JQ&e zRkMJE9GS*OduFZUcryv0C-Ahu6O$-svc_Y_M520&?h-C9r{0O=NI-xfBVe{jt{gt0 z0*gl|J;5t-tC+je<_ad@Fu#XuIcm4E_lO=@rcPETfWTk{3{dl-XBJ^U98gS#r69L@ ze~+4Ppa~(k`-1udVVG4r7p2L~>^Ybp?J_wFBIC02oQW10aVkAC+B8(0W;dxKjFkw5 zz5EdxXotrNQeTVsDdz0`Z2*{Wun9|%ZBcY~u{r-puStU2e^s8d7333563akjb^x6_ z-ElMP)Vv+@n$dBAgC-0b-y+jcd{W>2G|*y#jqP(vRg<<7?H_?Sv+*jNaOJ;B$U>K zQRs&HFiHJL2{c5YoL)t+cxAj4%!Dkqx|f6P?8y#RV{a_%Cz!daz*M~$#5;ks##zT4 zktO{Ook5~Yt+{vTn3;BGgtGy>cJpAlOhF(EX>WI?|@EOmO)9z-z)<~ z@EbU}%V2JgER6k(W*G$Kbzwy43)1lN;MwSnfC}FeuMaA-Mb@tztT|PS)Y@}vh=UCW z&FC_4mPgRBR-{NwguDKo8U#(S7=sCCV0T`2NfiUf;$nNfh*kg|+)6Mg6+mEnDFl!B(@F{xKsiMFif9}230}H#?kRF?X2V~u zSg!o?%dMNgG>ngL*a(`V4C_S`^kPXgsMss^)lxV~Xz=3a4`@DQ&BPxLL~zk$uuoc9 z^7eGXpuF^0;P5c zU2Mg>dk}!_ZcuJNu?Fx!9K_N9V2@AC1$$gKdPt#71Xip##?Vt_PQzYAm%%1l9k8;24)lTw)t3NbWu$&-*Irt-&!y&6adxHYA8ho zz+k7%SR#WT3UP#s39JXGug0+G3{X7_sK}DF0aymA1JKZu;n49c2()VqQXmj>(|Shr z(e%r9^nA3g-6UWxbKZ_9PC5_#$`|bN9=q&nuZ5#e565jFdlhnNYWWecVsm8@S4VJk z*cXaUKi5bE})C_OQKZi&NMJblGYQ?5We} zQ%rUKzs?}Z9?YZr*bp@uT@LcGwL&~Z0%8cgFf}N*LYXB|CFkx(N-Xf=*F}d9pF9HF z@*{Z;xf4Qz`#TV4RB`~YMuZi%AS5~k^Xlb5{s?>Q>2_M&E!DtN_+*#srnGG00NML1 zpb`;S0n>0{g@$$`r+++j6tSYai_@Z3YJCPbKp>d4FfVItH=o0Z(U{ZR>=h?DQ)xRp zSQ-K-MsaF4(#3*S7w8G9)|slPb+f>|giWhjkA7euwL~N;tveO34K}SNDjKOK|+nx7?i4nrW< zk6HmTq;Z3>^{ljsovp8gUK`e5g|z~GJkUx*p|zYhiYzLkF)KWaa<$Wv$WSS_od$*H7`z!N#-4T)n{=2}-B$BR*83o8 z%%p|x(rQ+kbU!?NBc^G#o-)*xFt9vGN0hlH*#*Mf+$5x~qXzVmBFo_sOg{YmScYin zt#p~TRo%jtagBy8Q@4m_53ce$C<>}MkaP_S+EUcL^EvI{ehxIJ@OcZb?n~MO&bc4K z(EB5208ZLn~|w#b+VcOQJiQEPrmy>K~z*G6GlpD4_VD$S=asswxcJ@ zV&gQ2MJr9aVYGid5@CH~z!SWO8Vz&BY*@K!CeZ5N8Pw{tZmE^jSd01!c{-N5XSjE0 zy|oeU)tL_W!7R1C*4cT^TG|P3!2Gh92u#GNI4lbftv(%Og&v5wZ*~Nu_vx%iknGK~ zn)|#&Ac@`X)CnlfZ|r8(5-q=?5hVF7^-HBTEA3{a6KZ)(Y0x`#2o0)2#Ud<{9Eo6l zYZ=0z#v0kUOPEX)1O^1Bk0wrJ(UmLLUb-1p+WpQ$WOwQ12(mLLvcAu6CQmiyJ&ONRcTMwGRoRg|KsEyI|4($#7F<4sY#7BzL&x684BAy_` zZ7eeWrN;~vo7xrUA8X_}cK7}zZ=o07(;mt_?H_)-MTINiwz>#=>~6J;MeWZv(FG{1Uo0WO2 zjmU}?t&cFoLh(n=q&wI*oe3+l6_Epk&azHZU&yRPd3f}i(4^n(tSM_5&27t=@6qw% zV|5o-*tUXE1&Il6sHueR&-=7L+pZ1BwqtJ>&c3o&tg59Fwm&*MTIzU7y z_IR298r%FhGadLd*WY%51fULHn_+-pRK`@a7`2?EUdQ;Xbr#1FHDqGU!8hN4`%Pq> zSJ2v+@h-GdU;#0saEv}!p-1RJ)gzyszHc^q;LpBB$n`vJu zbW=-0BYvU*SNPa58~RNw+c!(ey&fWJ2Pu-k(l=@iw8~%ARJ9*lOHXU99BM*tWtpXq z!c{b88CJlFc@Du*nTm-`4-;A8&ZTNrbWpE~GzN^AwoF<+Nd2DO1A7o)W^5M}{>vJm zN|>6_wh?xBXGz-tGKYC;&yEfxB--W7C#h$DLWqY>4MSfOM>kZUl^txC_#5kgmTmlw zjW%FX76MQB!$ufbeAzCW>$Svw*upx{%BqjW*%f3Q2Xntds{%$p9KkuUP#^WQio^9C zJCNI5!kdJMA9U`~EZ`yS;&6?`;R_`89yGkQklL>w_iI?Kw;g!vBiX!#DdHchM3$+mI<5uWMr@usnvv;ZS_0_Mzsp+=RiOvKGwwOXtqLKk? z^Qpm%ogAY;Sk)`!3fKBVio`S0Ug2ulx1xET-S%NM9b>ndS|Z83mXFvzhSM{4n_u%a zcJpGpd#ve*9ie@u({=Qwph+SfQxZd2MYi0k`2%Wl9T@WNn1tU^NEhjZwQNz2`kf484LY#f=I_|vl0II5Q<@O zTKX=0UCFFT3>3ps{G4!S@v~X%=HqsNoX)@icm~$#y8>4eq?s!kH)ych5R?R$!Jzp4_P%zJwQbW%wk_FJleX3F%oZ_Fp?eub>%IbyQ=A1b)CTC)SE2eq+rz*+GX@Rw@ijE|La*vD zp-8RTh>|A`t&dh5$MY9pKb*F`@IcQZVzw8eIS#-*+Y2zcvvAe+ zq6ZY3EU+@=@KE5veSlS2TIK-^Z_Qbga#$gkrA$NZ^!#fuE|;{U_tDOROVhat$>K|1 zr^PCBj-1N0*lk+!nlcy4xW^Z9Ft@OXS2h~UI4sd<>O&o_!U!|LK$mATcK#Y~51>)d zSmM>b$N>SXfD4%)l3)vPX-OK^fc0Qau~w55nj)YjE~CroJuG~EpRW-Jau2W1ZhP5N z3Bb#^9aBLufAZ3U-x^#(y-Hu#Dqa3$Eh;Ktky$3wrr>O%dp)*8kye1V@PmD=9c{a^ zeF?n4wMNaogk}kA*J0wW!WOERL^P3)1e(}F@DqCWgGK^&oWsOfi7wR6i?G3eBx-HM zvBN{m_nUvRvk+JLn$W-AT!g*vF#Tk?%eiFw&?j1Fydz67DcOi=FN|qL=0bnc(O4_p zaUPoTSfwI(%d4V`Uhgp3$Q;mm$Xab1lL@8^2x!NIynQOc1HEwFK@VH<6{f5=C}P&5+Ayo=T)T>-$ruAKY(v0aI69{knWy^qVG^ zKT+=k$db-HxL^N52=i)Kvy#Ic#ze%NfT6EJ7!>%xy6=HL%AG^o+V9%IC+v{xwo2iu zZv5}eA1P>%=Yy-dj;`d?u46|W)bGb497bS!f!+)uU1&Ssi?oR|k<7;m_G12Vs|%Jr zBvMFl_aI6MKjn6Dhvy>gFsf>?Gndev?p%7znTt4G zb*?*?+xl0*&gsiDsI?PSvzm`A{6ZJXYN@Gm^vUW#{Rw9-J$;sV88u73u3iTH17Iza zFv*;R2~rXb)6N1DMuW=H#xoq}vbN1#H>)#E3Y;`-z{Eb*`mI+!P}KD_1#P~7k?z9# z7|-8FqfWNK*q90U zK@6rsD*p;<^&(bUU9a}F00@Z<^m>c<$zW(!84lF&8=8f2s07j-JFke~wf8KiJ z&pqC8y-b3|dIHVdz0BXuirZU-{zi?b7Ia5%N%rPFpEB;NyO zBh6xdR-k{|z))2sPN6aN$&*V+2T3#G-tx{ENkbHMdkb`F`|O$v<9HaIYR{cbGhhNE z3lNF4%l)0$H!@;4L#EUYtiW$HO+mf>sAYmd15D%fRex&uxG?7>EQ0iMCAt~&l!*?E z-_*_;zX4g*SqnQC$)2MJPI6G}u0_Y)fhqbD>YDxf0MoYWY#jdr)5U~f`jTF?(Bp6D zTlC-ve8leRY+`wI%eUcw+=t7#!3TsJP6PE9)eI!ARZEL*!8zM$fFWB87qy9+8&t6v zK-zM)&C&>QYXJ@32P5x(MN4+cGNL77d6Hhfs~UpSVqh8R;u$B)@Tp&plp9Ewk@=E~ zukaCe4(zv)GP!$Xx&WWp0s#Gpp|=cnu{10J2=s zhcGo)rqciy-E@FJ6E)G5a&8*@6VA>g2QYMJ58|p!;i_2w#*JF#xC{!IrQJHsPX--MWFj+n#mHL|uO$vm;bRBw1ne{S!FjAS zYF)x|a64=n8G)O_3Lmvd0k`D2AO5op`a-<_95`0=9ot02QC(LZd`7$lNPp1!40le1 zDr5$hDke{7Fmcq^TEHEN_6o+SSCYEYMxJn_S9>^R5SJSuPtRF1eJxlOUw2@Uigt{* zmM*|bUW8#i;vKDW(Xj*<*c}NzoR}K@BAP_x>u$zkH9z)4Bphr9pZmyRk_a*(&ga{l_ zEr_{aZ)i2h_&|GW1r;8Fvo1HBzbskqaP%FexijM!oMl5qy26g^B^;2nSu~Y^2hpy2 z;qU&tG(E622XR~T6K-oRl3Ws#6Q#-H_a5u{0L&^YVl-V2OLutyQ_b`%Bdg;iZbEQ8 zn}=VDZsHb*d8i%!=;d%&OrZUZFgw9W?I-8hof!c+uvyNBJzIxYvY@-9Xy*AokRtp5 z@_lUd24wJJmLU>OfnL1}X?O}ACl1w(&)s^TQqu}s$?MKht*ZA4>>x!ZPmx(ix z0>`SBcZ2%tgQ%rAFAzHYDRj%Obd|*f&aeMI%asX1su2Wn3dLzp;JsKBoa~{rnGub& zG5i3qvSE@KPchCGg@r0OlOcItycI(hCV-V)m2)AiE-GlWO*qY+Q`cB`!iIv&k+vg9 z4k@em;BV-MbCpKQozl)WY^SI#ba#MNFb8~KnlDaevYGr`vta=|ZX-2pE~t8d;lYo6 zh?@>w9c8DK+tDT*a&>eX;CU`#6E5M4;jYWzhDNApBC1}3!>A_zbK(Q)yV;md%SeC~ zoFwoUpOu%C&5L1gp=}Ub^ly_MZ>?y$5b#h`yJ5h5<&0dZtaC0W&W1K8Oo?-0Yg?(> zxUOIl)kws2tmx(@wijAP)|ozAS^z1P^1Cb_La9lfmzNW7uQ3m7E2>uvT_JH4~a{188ubbN2|z#H!Ds z=2A4yiW$`%oS1oEvzwgdB+3Ca2f+=O3nDZ;lTkKtkv0keJ`-)Q^-RRWLSCUOGME=* zSzEduRAgR~Np?p7DJa?KLny3cz`u+kErO2GB%8smN&wGej~M)n7HmW-Sn;w91-7x3 z0yFmz%#t|28r*ZRo(#qbS_lVfn9Y?hEAn6 zazX85f+4&cqDgkZW7>^BW5t9>03$5F7V&I@e%& z*-j@&8Q-L4q*|m~V}TX5-$4w6`LyY6`$_O|Sut1`W-CytN4d3?Jk_^9Gq< zWAYGWgIEQy>(7r5?E*KC2%hB|hodVt`pO zCo(WIY8{ktI{qJVE~u?5e+G+lo3|Z|8nxlgx}f#PO%!KcCD3X`Fm$laA!<`MgqUTU zb1||XikKqiA7SE5$NfA^ z$AG2$09~&9DLTr7GjM&En;*|vc8_Ce&T+UV7`xJ(_#3kO6l>}{C7OdlO6oA{@Xrh4 z%Qx^(uUiMHf_+tW8&GcYl678Qg}o-B%=DUza=zD6l*_%YpuFbw9_2@`PbgJ=yv|Es zOJ5gdqP{80=K78(JL~78T&Q1&aW5 zvVnI$lmom6p&aI&fpW6<4wOH7??So9`y9%P-q%sy^uCGmw)bt6cfIeTdkgE61)Tfr6d?o8U-Utvrqt~EI3ex2%#v$g>aNb1>A*DOQ?miwonIUT>;l4 z)EAObHWhlKOcO?;94kyfIayeMaBUs2u`aBac^ z;Q`7=!XuQw2~SbJ5Z>s#h>m!p#6Ofqf-5Ax#EddO!F7@_5{B{-!LP)8Vm_3?Vlc|0 zVo{XE#o{Pq#aNW}L|m=dSnP_jyVxCNFR>TOG%*e3AaStHTO1+|!S+ybIJU=#V^J;> zSLnPXNy0g$FbRK4swuTb*-mPQa-=jC38WB%8$|~?9n7m=VkCVci$ zw8frwhW6O*YCx?5|BL!G^fRD+8U`CspN1g@)Td#nVJNmo8qhWjV+>=kJ;SgB+t~)x ztznH}Ez0c%v=hS~!yc3e45$agLBl~D`IF%%Y#%Wk!S-1L+P2}2;SS1shI=SgAG^-m zr;HD-*{7UOIc&%H#9_Od5B|E(FrQ)A9_@pt<1^D|Cd&Cfc;kF_`RvBGdwuqyJm7=# z`<(VUgYAnxS5e;ZxsCFk&wcEF>VtardFAsOAB9#r zPhXt3PkmYG2WhcBT3RkG*O!-8N?Y|6r0vpPeNEmb^@)Z~hEDpX{F&;T87>(v>zf;{ z8m{SE8g3eH>QfB24Y&2JeH=axeH))(pJ07k9{_&!?R<**6xFx)DdAH>-@&J}Pg#9O zJfn*GEpm>7SRV?0;I8e8`USySn7VKQ8T;womeV`sc5{{Q5sxFZiE+<@d?#Z;s4fkaqrG z7jsuD#`PNEwv%8&GH56WH;w>969w3Gktx7iDV z4PTZ*mh3WxY80xGT_$hgTZP&cS}I%Qrt-;KzNg|~lM{{1hr(*wmoTc9o5BiXhkA97*?O9k!9iM4dH2HU#X!*gOCH5FY>Y_NgL z|84=Y|ML=T=zb_TAh=ENk>KTjE=O{<0^R>4XDeTp&kj~YhVfQ!9NqL{Mlk%1&5B_z@UcmR|mH+zf-}mH% z^CkPx=kf%} z*b3)kCh~78+A1EaG`n&N|Bipl7k)hd#FyC9RTkuJWq0`dvOcXMEnLw1B^Ay5!Yulk~jx zm$J$6mc7yfzO=cvzU_v6+5S(~?Uv>(OCIDLX?HQ_2+BAAxbS`L<9e%n{r9C^?q8o! z3rG0K;1OQtEeF~k=-}U>Q>PMLa+iGQ8VH%#{ZMz`oYmmXJm}!cbgrB_hxHOY<3ym?|9x7oF%V~@J}oB zc)lI7)A^b|cl>4mByZ$dn&ZHJv;}+ny+u-Ra@T-y1q`|Qr}Lm*LTo&();MU z=*Q`O5tX?}UjkbHRegf~I-)U0=$T%_=Ui{2eyO*wcTN2U==*i`kGvDT`|F>25A+`7 zo!@(i_ge3Q-s`=$c-Mixe*(6EGtlqbd*AbZ;N90-6TG~?5d?vF59J!Y_i$+RzTP9C z(VM+T3;6|`_ZaB%O}wWHslo*B{X&MY-}{2p6kCg}g-7Blah>p3JRtroJQuHtw}sc@8}Y61NrYKbP$eRX zf>RnJjS-kMUYa0i+>#*V!jh0F{2@(~rV*VqPnt)(q=nK#qL&s+i-|Y4BoKjH5(tqt zN}Grz?Ua5bKGGg(FEK%1J3;)UQ_?9Sb1MM}FcdNrB0DK zw=WrnJ$ZZixtBsmKBl{;`vnowCv<)FJ9HiRZ}sjaTVepSvu=~ zax__^>r2)`wnBD8_CWSR_UZbHm2{)U%8*z{6-XSUDx?~uI-~|99?}q3^dDZEUXet- z0&V!Xu7g*|=R3HJ^%Y%T@5#9Ov5-t%2SkQv>gxaf4!kPp>i>Dq`>?$qau{+9@(biR z+>jFK2_LG#*!2 z6H*J30I3bB1NjP47t$2c4ALCZ0@4!F3X%e84QT^u3uy;w58+EkNGC{VNEb*~NH<7# zNDoL)NH0ilNGc=^k`Czu=?m!x=@0n^G5|6VG6*slG6XUdG8{4IQVbFW zDGn(CDG4bBDGezDDGP~)l!KIq#6T)SDnTkkVj)!^ageHzYLM!XL6E_aArL$_uVD~8 zJFjmcBO#+8qakA;V<%71M(~6Cgc|6HslWEF618MKI8%9A>KR&b6vKIALJW9o zLPf44jAC|8Mo|?9N8=>HYrhe!e|D-8I$KU9aDJ z^{Tr1pXkB!VG@wHMc(ujM66$-LPY2th)4w<@Ix~|MkMkfkq_AcI>H{%8IT8h4kD8J zR!yKeAYbZGSS}(Su8W8B@Ng^-$MSF<9?pYzQ$@Vj0N2zz6P|~aPzq~ct%&%J0qWOx zkBIn@8{sz}4E0d*PI$)ysvqGF7a;RHBvft`j%jhY#RG zemkB#CXv?^r%>I6J(*Wgq!#e0^Ihu1utPEIP|SCg6Z2i|#6(6tIZRWBJJZxeNai$6 z-OBoHFdgoqv~fOX!5k=tdGHY17eM8aJPVv@o`+!}tb|fn|HOTuhmZDRVJpj#K7h%3MsDiz#z4WiC#?GL*NN@)lFxV#-@gd5bA; zan(_?jW@Z*Z^7G82Jb)ubRE10Y4u=~^Zg_E7(Rh7;VbyYiOVLH^>haHat=&_^!loG zCA|xghew?FZY@cilEf)VyjDr#T%&kpoyO~wq(12>p(aYGiIU2iD4`}wsEHD4qJ)|# zp(aYGi4tm}gqkR!CQ6W=^dl8f8zt063AIr|ZIn!~Q&R&T|* zoO|wa(axO5ojD&Cz{BteEP_X22|Nam!xQ*oPxAQ`JPj*=d*3=99fBT8d5J=)ew?4{ z4_0if@)D)HHO=c2Cz(E{$m|HGI<|SYbG^C2iJ0H>xyiZS@;SEUhpSn?2ByHZFcq$Y z>){5t5vIXS@OLPJo8cC?6>fvuVLIFacfwsT130F258Mkg;Xa7NESL>*U@kmJITkw+ zs|1$7W3beTTdz1XtXE+@yapTKb$A2bgty>rD1&#L8S)t?BA3G(@TL<{?a*ENS{>p9 z(Ph=LbV=&^@p(D?4PtNwOd;Jg(r=5L&F2_ss0QV0iWBz_bt0lidQDVa`D-}qDNd1l z_WEpJW&JhS0M%zz)Zkg@*1+G~yM33HSoBRVsg{>|OS}8)Y7Fg{N*U*~ZXE^PI;y<> zzSMO-tVZeYcH)&1A4lTjRcCTF>(>CkLgM2{d>o07Bk^%0K90o4k@z?gA4lTjNPHZL zk0bGMBtDMB$C3Cr5+6t6<4AlQiH{@kaU?#D#K)2NI1(R6;^Rns9Ep!3@o^+Rj>N|s zk$5c!aU?#DEX0w8I1(R6;^Rns9Ep!38*wB)j>N~eBJn$R6?aAke*4nzUq=SMM-G0E z^}hoeu9hy??)t5Be-~EW73tb&u~UdHE<_g>qKgYFb#alaq1LBqsn=P51KxzU;B6>_ zcbp<5vk+Zeh%PQf7Z=u3@>bZeR&Gq=apU_uVBY>@o6unMi~xqK6H1F2oU0P!s_52Jk#0Tt=3v1)8BHq|?eq~ZtJsKt(fE<{xyCuy zIOiJYT;rT;d^?V8H*l`i)^vB5prNb1?Im#IpIj*|F@?lNMi8Ug4@SZ$7!6}!e;5l# zz>#ni{0WYRV_*Ut3&+9nFcD4w9rHR7PJ)x+6gU-5gFnORa0Z+Se}S{$Y&Zwb1^s^> zoDY-W0*Jzea1l&~i{VnZ8m@sUa4k%Q>)-~M1+!re%!T`*80NuzAihe(%qC)H6EU-i znAt?kY$9eh5i^^JnN7sZCSqn2F|&!7*+k52B4#!bGn%T*|)N_`Dsa!yUlyiK5v=(QKkR2D4IYE6hO$5#+0%sF}vx&gjMBr>9a5fP*n+Tk(Y$9+r5jdL&oJ|DICIV*@ zfwPIg*+k%MB5*blIGYHZZGHlu!e@ZY5P`Faz}ZCLY$9+r5jdL&oJ|DICIV;c)-F*v zn<$)36wW3JXA^~^M-PwpS3WC*O(f1H5@!>Mvx&snMB;2BaW;`Sn@F5ZB+e!hXA_CD ziNx7N;%p*uHjy~nZAmx50yPI@muQ?#G|na(XA_OHiN@JP<7}dFHqkhyz7jUkIGbpk zO*GCX8fO!Yvx&ypMB{9taW>I7n`oR(G|na(r)7KuI%Yo@38P>%jDh`OEF1wx!cp)i zI2wrQp|Spm@e}{}BgRkMeGm+WAutq%!Eo3YE`*DK81Sx&@z>B;90P5&JL3H@w9W4) z-k;Pqbz}ZXZS(t&`*(A-h_*RD`zrrH@eh2&Iou`Us_uPs+~dqhA{)~ zhI`;%m`rn1_*8ODkwb4g+y$E4}LOe?tLi>l%_#rfY2#p^?1rAvAu- zP2JnIq7!NFUJO`MTHWhuX|H#!>OOo9g<)_VR9jh_em!kjZTj_W`t@x3^=zy{%Cc(P zq{mv>SOOX^|U8{Kp@k;BOeg-M)YU3Md>#ENpQh65jTiEoo*uy=G z^=LA#tAR~6u*t^DpvQq|e;`@HSe3V;uV?aFCa+U@C~`| zl{G7jwOl znzgoT2lO#t8z4tU+ep| zx`ua7@^y()60h&qYB{v4YMc_*k;VKa&T4-B!XL3wLvMw5Gp+qY0`Px2ytWu9wkCmv_qq>EX^6^vBQm2)o z?@Bikl+vZ!B0cFPPU+2Ut&6&?l4M&dS<;%)Q>`zsW_6@zNA+b=b2W6N+WIo!%INmo z<9bw!9?_C&l_lFl$@5OTXREAN>Mq#2My`9`He_?_vsn7P8@9Qr`*(Y^czySDm#u1_ z`mI><9^IB&8H2Af77lcM7MoGxsqx&jzID`IN6)K9CBH#UyqTtxv|YR|=^HUh+Gdor z&2!Y(i_Pet&DgWe_(+?vWt;IlHsd30#z)$WkHl+L#b(hW?O1wDtv*(A+|m>7arBtW zn&Zf?WSk-~0O7CtrH&8ikxoz4ES2`Zup1*N&welxM!{$p1N*~RI0BA@qu@_)G!S+D zUH9E{J zVHgaDec?j52qweDa0y%rm%-)mH;BO%!2M!e1y=(-0S)!|G}yy)8xnszOouz*PPhwZ zfc9_i19~4C>D@`SGh%hx88!X9njX_K?zU$j;3t4m-PJDx=o;((1uF&{tV-L0nkt5_5`7gc6o15{{P9CT~fP6&~CrKr#@ndXqjg5|OII5rQkEk2bUq`+#!b?CzVHZ4ElIqvq?k7ts zwVS=Wo-nEG*RJ`LNm83dJaKw3-hl+{JM5P&_P6Lf|y&=oQu6M~Qh*^mRd&<(o7o)CgO=m9;U7xacc&=;-$n0N4ix!XOw7LtrQjgW<3*=>HKMWj`1RqhK`Pnd7I1@KZzhsUiH-5PoV1 zKQ)A(8p2Nv;irZei4+pY!SOH=PJjrU2q(eGa0;9Xr@^1$bT|XfgulR9a5kI+=YkFA z!TB%=E`TUp2p7R*xEL;ltKk}$0@uP+xDIZBSuh*sz+AW=ieVnihX>$6cnB82!>|w@ zfkp5rEQS(T0*}Gt@B}P{C*di08i>d;5-G$;q!1&KLX1QTF%l`nNTiS;O3O&35F?R7 zX)T8kK5B>&Ng<+lA$-&jBa%XlND46`Da44R5F?U8;tTkSsBa5s39X-#U^wgx7s5p_8K^(}(-8h?2>&#Me;UF+4dI`L@J~bdry=~)5dLWh|1^Yu z8ZxL~MkIxZSBH%2;ZCC64$x`uf*CLq?t?hYg4r+!=ED7e zj8wHbsUx&)2yGie+lJ7#Ax05}7)2Cf6j6v#L?K2Ig=m+C7)2Cf6j6v#L?K2Ig&0K? zViZw`QA8p01Naci;UoAMK7mi+GoYRsMHFHbQONun{sG^>x9}bOQ-rLf9~oj4QOH^= z3K&TgvN#4Ki9+8t6Hht4JebY95)3%#)n4H7p946;5 zIfuzPOwM6)4wG}3oRj0?=JHvcbC{gN*t z`;R_#wE7=?>eynm`Txul*=aqr_HOJ`*AevJf4};#|6_gXZZEpNvdq_tk1l!}o1O37 z$mj+Uc6$u(=W`y+hs96=OW-kh9G-xs@FYA1Ps1{J2A0FKumYZg=V2w3!YWt|FTfgj zS%f9|Nq#5!oqPk{gzrR{Cr9Qm{$4*J68DW3Vc%&Y%#5O8e>r@FH|?n`b#gpFT54<; z(E+z-OP`k>DV1vF)$DuQ*4~q3-$l(4X{p#&dVf#d7GQErSL*rMs`IHTyW2mTex|}y zr}Nb4*$(XztWFSn7Q~(fv1dW-SrB^`#GVDQXF=>)uBp z&w|*qAoeVXJqu#bf_RQ2sP+ATxD)M&KgI{sd-TT~Alx&(p%3(hF!Y1|kPmyo0N4ix z!hd;u@NTm*ss6qCMhDc4Z^!B#5wPo{JUc#r2>r^mJ$7f5$ENL3Gs-i~Sw!0-;)(CV zD9;}n>JOhdf}Q%G@`-n&hDyb#>v}9T$J}TQRXr+Y1?xIC{rl5U`WdUbPd)4cKf2yH z^ls2l;#fDzP#I0ozO(fnzUm=7GdiBRcvAECoX*p>XTub>Xn|~{0y7n-|!3k2e!bkPyvoJ$s@o36D*LRzyn_JfghScQ)mXwp#`*rR?r&S zKwD@B?V$s7gaCAgF3=V7pa=AXUeFu*Kwk($Kj;tJF%w@y^A-`;UxO~l0v+2)yXr*q zs^{HnR%5UB`_ujX)F?ITiZMB^-!8mzwKG+2fDhnXXR0p~g0RGyDlCpvNF1tA&*DNK z!jbOr3*C{Tm3wGiq(c8FISCR)=y^?;olcg z>LUF6BK-Rz{QDyO`y%}NBK-RzN>D@ziim`1zrF~+z6ig*2*17vzrF~+z6ig*2*18a zc*!kJE^%^+lS`aj;;9_mr$ORw3T6r6cy&|Ga#P1SUYz6Uh+CZF#W`M_p%~Z1iko2B8PxAy{3pk$wb| z^RPhb5#QheFEE#o;3<5;Q}}|X@C8rd3$BM~0WF~wv<9w^XbbIt>m)h=a}bFD>;avi zGjxHjkO7$xge=I09LNQ(vFHwaLJ0Dp2lRwq&>Q+dUkF1#=nwg@7wioKU>_I=gJ3WW zfuS%AhQq#4KpodJ%K$PGq0Y42N2s$1wHu*!Bh+q$+KnJ15o$O>okgg#h_Q-@DbY~l z1^AXczk`1QvSR!TzK2cl1N;a-!OyfC*TTolQm}<+WpidKXansb*NwJD)M03`(~;}5 z;oOS2vf&bDE4UOct0+e{BFIJr*@z$;5wRqFR*E_^QcSHV-`c!G=u=^RMC|tyq;F(J zxg6yjEk{=r$uY1$jD-W>KsX2vhH-ER914YS7>tL*X$hYNXS03|oC`Lb2j{~ixB#MX zAzTEL;bOQ1E``hBuW&gqW0Q<=b*`XXSMq%dW#gRWy{ylK`ydXpU^dKwxo|%e12fsl z`S1Wd2oJ#mco-JKBd`b_g~d<;OW-kh0#^z2d{`RokIS*C1k&jf4_^YOyD6^p2) zMa(nN*i0uA)OF0NcRjUx1D`kYnVfxU0<)Y=V3xB9%yKpXJ2in>&L%L+*#u@en}AlE zfL1Hevrl2GV%Vw}wkn3Lieal_*s2(|Du%6!VXI=;su;E^hOLTWt76!y7`7^gt%_l* zV%Vw}wkn3Lieal_*s2(|Du%6!VXI=;su;E^hOLTWt76!y7`7^gt%_l*V%Vw}wkn3L zieal_*s2(|Du%6!VXI=;su;E^hOLTWt76!y7`7^gt%_l*V%Vw}wkn4GnZQhK6PT%O z0yDKuV5YVSo>k5SW_UsqaeovdNkvFfk(|nv8h~Zs3W(0gX^|YsopJ?oF_r6*?gMDs z&-_k`xt+MSh1=i*6zg0Yw1p=!V^oB@qYw#<%PXN_iO?k(heV7+BF0rq1oMDg3VJ?} z%PG;{AO=^!6w+PiOhzKsP~yo*#2QL1DD^lbVjL1N4v83tM2tfs#vu{okce?e#5gq{ zU9gF?KfsUh6Z{OD;otBJ{0FwcuTTNJkOyfXAOJM5P;6m1-e2W^njkw3wlEz=nG-!2mN7zGY)AOhct{s z8pa_FOtG(XZhSf-ISpnKA`u`;gQc-Fb9euIU@`bTCvIVFf4?XPztLmHgVAmN`a-u z`zTW9lCurW>MTqtXtB>HzSW{~rk=*<*g@8*@tU*qc(F05XXteF*zMU;tvFg2qHrNx1e4)n zU^XFnDO?7B1wBLCe0uVA^t2EgRVehV)>UQV=y5TEJ2Zk7DUzd|5;+F;hp}(~90&)& z!7vUEfkUAX4g==z<`)ckpeOW#-p~j7LKyl%e=$xRfQHm|y1FrK3t{L7 z{b4)WTSB_`gTCV{uaL0ZdsN@Ofy(=|6{DVX%jYhh=$ip|!#!{>%!Ke_Vqm5zFu|{>#3Vajk=gdT~xN1c;gX9?`*=N!YV&aYV_g3jspgVuE~(~{d(3!G zm=)`V1rii^zzaU`LlbBU&7e87fR@k-T0cO;jk|hRIE4r zv~`<6Q)mXXc@6ww1Ao}SA2#rZ4g6sPf7rkuHt>f{{9zM+*faru*u)<;@rO-%b72o8pEa0naqHrNx1e4)n zxCAbR%iynYIs6S`a0OfmR{?D;6VK12tz}Mwo8a$I1UJJia4S5(>n9(ChhQ<3z*2b5 z;3cE9i+$jSCeRd`L33yUEuj^(hBnX^+5uyytPao-0J1Ny;XXS&7U_WL6@xa$hI_%8d22 z@g{A&NgHp{#+$V9CT+Y)8*kFao3!yJZM;ca9twqU7>tL*;Vd{C&Vh5mhV$Tjm;}fg z{;!SyYvcdg_`f#(uZ{m}thic=Y+IXlo9;%IpYU82Wc&IiWs*Q(glZRN;=w4w3&#F>eP5xI!YSdftX_q;j)le=;)%o(jdk!M zyaX@9EAXZ}<}z%&&1V_B0||H+-h=ny1Naci;UoAMK7lXcEBM9~9e@u@=H3um~Q7$AS2WrDx`S5}tymVHrFF%Yin#wE~`l=V2w3!YUxnZM^_% z;ANmZ#7GNy??Jd$oV1V_c|>wN`5jmGFr@Nyr1CGUp9N>Lel4H00~PH+yr~sbHQHase^r0^=UI7iPkJ5QkYX8|J`V;5tyhaq2ft9TqUosDL^w zpbiVD!vgBCfI2Lo4hyKm0_w1UIxL_L3#h{a>ac)1ET9exsKWy4uz>4W;Ew8TLYuld zw1M`J%k}GqmwFi2>U8(I>3)X-V#@`@mJ4`mLjmVl;9boKsgc5@rElrhJ}B^j7kuD{ zCeRd`L33yUEuj^(hBnX^m`#S>fr_xPvW_#m zH&l!jCgbl0>X~k|)KwI+t~>E zyaX@9E6LfLtu4YQbD2{*Ij?jPnyd&-RwNEZvbU>8jVpE*SL`gV*jZe$6S!h0aK%pG zik-j}JAo^90$1#W)H$MgY&4H;VK31vHk!pov)E`B8_i;)S!^_mjb^dYEH;|OMzh#x z78}iCqgiY;i;ZTn(JVHa#YVH(Xcn7V(WCdy;%=z!<;&$ey-?JWjb^dYEVkT?P1TX- z!<@6IwW-uv1nn}FT8mI?w5RETp;yjteN5km5Jf~e3%N^4B^D9o)OJ5kdz^ep)rPoqj68I2 zOQAfAdpQPIvQD0O1;jy#H`514-UZ}c!2IA5bud=_kP4fbuQw9+Br-)kwnsbF-#hs( z+usLb0BD#v{zN%8>m_o13SZ)L)!*5q22}jyIkh6@DdMd|SO(u!6}%m&BDTwO6>hFq zqJxNlf&>K~@PZGROB)>&M+e2xL2-0Y934boG7vpQ2gT7radZ&9sX+V|9TZ0g#nC}= zbWj`}L=OY#NNZQf0Q@9$P#hf;M+e2xL2-0Y932!#2gT7rJP87M&;xoxFX#<@pf7}> zAM}TO*bDZC0k97Ygh4PEhQLr52E$=rD4-5#lX54=xRYbt$uaKa828}zjapsBJ-ZrS zfLHnbdUy>s!0Ye^yqSCs1gm3Xb!@DTjn%QSIyP3vMkZ`z!bT=+td5PwBJ z1pHB~j*ZUL7JMA{SrPH_n6#W}SaRLtSv_Kwo?q^{AaeB-_gE3uCx{#cu|7epkB#-Q zy-(p=J?+kk81%m2#Qa!k{HWx8gRSKl`aa?LM6GKpBl891Xd|iY_bO+F*W;`ZwE{JVHgaDeWAc9+W|=lBPn4dC5*NXqpibe z>oD3njJ6J=t;1;RFj5pjiXuo+1SyIjMG>SZf)ojS7l9NBq(~q|0x1$mkwA(BQY4Tf zffNa(NFYT5DH2GLK#BxXB#Dc11mO8Y17&!?ed0}-rI`M2hN&?AKB8>G6r%9qf5(SbNK@xSJyOzWV zk{A(w^k)g)U5S=;X;f@-<-EOfnwDvDYDZ4(Tt9Go#}-uIsc&*uz6Eastv{}1jB6R= zS`z<*_ksQ|u4RmC8RJ^UxRx=lWsGYX<66eJma(ck5iPGjxEXGNTj4gi9j3z_=&iktR)!wIF#`63kuVBI!x-2f#sXz9 zD1$*649Z|o27@vfl)<1324yfPgFzXLBj89l3jPE~!!a-cj)mjkc$f$$Km<;NlYlsi zK^(;(j$#l;F^HoW#8Heh;7lNnVh~3$&W3YpZ?DTaA~ zMK@^m82BE>L$ClIhK2A5EP_X2F_ZwkUk1Hj#^dk=ECpIf1}!9m7Lq{=$#@2q!?Umg zo`dIMC6vM{Y><};8QMY)po)$jtWfwh2NY;Gj~f58v% zBm4wE!)Ew5`~v@hEu{aI&kAsuXGDMjCRiXrfd_bFt;HK_E#6pb@y1$aG~8^qrY;_n9WcY_x7fxjEX z-wopL2Jv@;*8BLoAHl~J8_@zm{L8rXamP0WtqJH8pjD_xA=$DM+cF`SbXp_q$Gph8 zH-so8;Ui=O#Hf$+^js;AA)jPKDFp&u}`N0cXNr z;7Yg(u7+#iI=CKgfE!^N+ysAzBDfiDfm`7=xE-d$9dIWwAGKsYYJB-1v9p+5%KDS= z6fA=k@Ekl3E1?ut!8&*mUV@k56?hfa!)x#sybbTdd+xeKl|Z#;fw1w)7@|n+zT_|K8V9C zm<@AaEH;?z+bUpG$OFit~XUvUDqGlA_)U^^4o&IGnI zf$dCSI}_N>1hzAQ?Mz@h6WGlJb~Az9Okg(?*v$lXGlAVqU^f%k%>;Hcf!$1CGZWa% z1U55)%}ii36WGiIHZy_EOkgt;*vterGl9)aU^5fg%mg+wfz3={GZWa%1U55)%}ii3 z6WGiIHZy_EOkgt;*vterGl9)aU^5fg%mg+wfz3={GZWa%1U55)%}ii36WGiIHZy_E zOkgt;*vterGl9)aU^5fg%mg+wfz3={GZWa%1U55)%}ii36WGiIHZy^KOQ6pZ=(7a+ zD}nw>aNi}k?-Ja1iFE1Z-brxJB)Df1-l;Gj9)?w1CzIJPZ0ezi+F0ZKMBL+J=M(iA zE#J?bTh$lNx9Us2e#3C1Qz@;d zQd&=?w4O?7J(bdWDy8*QO6#eV)>A31r&3x^rL>+(X+4$FdMc&$R7&fql-5%zt*262 zPo=b;N@+cn(t0YT^;Amhsg%}JDXk}FRe(I`0X?A?^oBmr7sAjF`a?eK1$)B)*arr} zAQ%ioU?>cO;jk|hzz7lG8O{Jv(0~{PqhSo}4`bl~C(1n=<(`dl&qldtqujGm?%62! zY?OO8$~_z9o{chZOe-{KfO%sA%o`IBN5e5N0gi>^;CPq_CqM*Fgp=T8I0a6H)8Nl= zI-CJ#!e8JlI2+D^bHRr5;Cz?_7eEv)go|J@Tnv}O)o=|=foowZTqkmwHzt6tiK1(w z=$a_HCW@|!qHCh)nkc#^imr*GYoh3ysF=kaG@IYdfw^!$6vI524-deD@DMD3hhZT+ z0*l~LSPUhw1RjIO;R#p@Pr_61G%SN>U^zSsE8sbJ9#%prtTHp1%QC>+G6Ck62{6JX zz}zwc=9USFH{mUK8)(amFW@T?F#Noyq6svGX2825j21+7Te7bew1zg&7TQ63=l~rd zK-xX{>;#>m3v`7H$b=wdK{n)6lo+{uc7yJ)CxjpmdO%O-a^{-}@Vsb%=S2fXUkF1# z=nwg@7Yu-XU?2>F!7v1d!Y~*P`$B;e#STQV15xZi6gv>b4n(m7QS3kzI}pVVM6m-= z>_8Md5XBBeu>(=;KomO=#STQV15xZi6gv>b4n(m7QS3kzI}pVVM6m-=>_8Md5XBBe zu>(=;KomO=#STQV15xZi6gv>b4n(m7QS3kzI}pVVM6m-=>_8Md5XBBeu>(=;KomO= z#STQV15xZi6gv>b4n&zpC%`;90b?>C&&DNyOdFTMU)g^-pMQfGTme_YRe;PJ$UJlD z1Po-JxpV@?b#Of(_r{Gd4Q_(JLlN8zx4^A%8{7`l;SRVH?&5ik8E`k;1NXvAxDWKR z9J63H%z?RZzjHHfxSMIi-Ao(qX4-H!(}ug5Hr&m$;chk_hK2A5EP_X2F_gd(cnltg zCtxW&2~WY(uneAoUn?O9HQVmT3tvllm~J59T-um@{A|+y`-(1+xJiWzGe3l!=bw3BCYx8wHr#D8SrC z0p>OeFt<^Fxs3wMZ4_W`qX2Uo1$Y`Nz}!Xw<~9m2w^6`Mz`O7sybmA1hfoe5!N>3k zda?Z7pRA8aSlOY#YzT=-T|e5)wFRg`Cx1FAW+fR@loe4|>6 zLsVO_2mZ`v)n4>b1V} z5QkYX2Z~`HJVM!*i+~4N@GLM2JP*S{SP7-D3f^|2_=!;u_3PpKd$|70^Aup7r+{ap zXvbVn0p@xN;73N8?|af~7uddr?`uVXxuF8g4HaN+r~q?A1&A_k`NRAA!~4Rk+IH{j#y`9-tZ*kl)BNFm z;r;yKef{Bm{o#H6;eGw#eQme*#b}l{iRHcpZ$lZp0}0S$WQgt3zNs1~L;EI9`zB8N zCQkb%PWvWK`zB8NM)yX?@dxAhgN$Jzf*dAN941m6cApV375$S$*o%mlnDi}MAVGl# zyx;>rG=Zkj4Cuij+7=<&79rXeA=<`MEzlN-C=zXp5N(SPZHo|X<5?Z(1f79r-iWqE zh_*$Dwnd1xMToZXv?b&~E_8$LuqT8d4|+gP=mou@5A=mF^n?D84|~DhFaY*}fiMUL zgYG{c3d3MH>ma+`9kb5PDR`J7cP!&t==Ca2e<)v8J$JKb8G=z8AsG za0}c4cLEWAMZ{mt1S0;52*0`?=F^+=FrS-vTKfn15q^T7VKe+2eu4kM7Wfq^z~QN9 z0S1_0fdmB}@PZHg&;*)7GiVMipe3||*3bspLOW;=9iSrwpfhxVu8;>kpeOW#-p~j7 zLKyl%e?Uq-i-6t<&kIOwCtV}vSl(H802~2-f{AbGwsYoYRw`p9-AkN`CSQK&flI^6Kk*Ax7WE)@O0C zc^=N@u8eR8hN-tWd1@UPbnDF3jc8t*I%6!?$d?3N*JIWl+Mnq6k@ReA&tLCaiPzWq z{$Ks-tkH*a0$io7{5f~(1P)KDY$c&^80$mIqOGOdw*6dh_s?0UKPtX&IA3Rx^E}6W z&6&wEPDxKo{<7wh-mj`Zr<`%syXnt~Z2d|#a_CU)eGQ*??UY;p?#xI}<4kuRO#hy~ z?z~vz+vhcZSz});`OTp}c~_Lx`BK+n>IhDOUU$MyGrh(6fot2G&yC6bPVz(VDPR#J zU7^#He$%u+Cs;xvAWg`A+vnQ&#PC z@~c+2VI^6PZAiFnN!D*$vfY!)*`V{SU=)1A`8)4()wejW=oH(erFP931yeG}`~y{g z4t}`4bDT68NMEl?D4egIudBaRt{q%^OZxrfl-J(-+ka2Ld$5?l<)4<22>*5eg7t2# zn{KSe0-8_lme zdA)0uK1mTJ{hkDRL;`A=OOO;;RI%Ucq(^il<2K1J~+7hC- z?(AQ^*EF_GxkhTIYD_!VPFvsiv@2NO?p?SoT@H3WTW3K{oQG?C-JM?|yEF4`%Uj!kZA=+Y-Qf+VryaEot*wTC-`F0s zNh2k7HtF9wpSa7<8p|iSuTg!u^`F;Dk{-gSil)k^Qb>`3$H6xA5)VdwT9=oyM zZreWRo9cAccWm{x?OUsPUpY@X4^{m>Qnlu;xHc-e&0W^~WzDp^_M5XbnV(afTGQX1 zN!wF`WP87MxzcpBsG#Qb-_BlBB;d5loCxh>6ohM|_hW_s+EabpJx-F)C7PCW5%)wxx#?M!}2yYF%)z_BDgk6>K4zD<`>7UlF>xS<6#;!?~XFoYvnDiD-D%#W7@3(Rabb-ic z_T5r-jrG3Dr0Eh-xv4U8kowl}XR($neK z2zv_kwz^(ldP+A15>We^rr%*HUp4(Y*Un@6zB}`E`iivPuiC$--xQf(8DWVhQc+$z zh5oK>boHMzuzG#R*K`jp*EEct`g3|N?lq0ozE0^W#K!7xI$ioT)O*suSFA{;-F4SA zncuEiR@)S=1=6Aac0x-fGuUm*h9um1+bOSo(0Y2dYA4x^-+3B#xBl&#I&zkz3hBgB zwV&Fu&#&^Y29D!{Fbek zGEE`#USH{wnxtZVefv1}#@e}J8EbE+F@#X&s$5qwf(zH z&-B>Zk#_O^fbi{}R^IOLb1N$28@u=#>~wa{oAqX)>fO3aH`Y4}DYi|Ah$K^0_xRF+ z3~tje8%VT$QH=&N+4Y;ZIs$grA9DM1tZ#(r_UH27`Nb~Nb&V8v=h{)sqFe7@1NUjY zX?Jd8dZaczC!R+5HU0bVWqoI@Y0dMfrIo6qTh(hqtf>4=#QluaJE}+T3-UHBAD?j$ z^l(O4#^8VO&-wZHROsDf_jkt`rk8Zr`faX1Y`-Zj?E2woc)t0?_N5#hK zev6c~uZSh1%*md)hV!q^rP|V0rZ&ajm~H(IQp^%Bi0RDE{FQjd@SEes2j&s{hs>k+ z&ohrUCz?G>W|%SinWvannfc~5=D*Fu&HtEN%r$0(HOX9OMXmd-0&AZ2pmm|!mE znKIK_CUfK{>lwMf+~4|8o-Hr8%H@^v8tZ#`oqXK-NiLO7%Ut=4{7{DE$MSP|ko;19 zBMaqsa-%#_8OoAJt0t<6JXSSVt>tm5t!gJvQ~?!~C##`qxI9Z0r~-Me+E0y?wi>NQ z%k$O#YJWLN9iR@77pOzkp)#rtQ-{k7)p6=Ld9j+PCdy0H>FNx5srrlJO|)u~nk27M zm#E9+)#`F}xx7|gp{|fq)f9EDyiQ%GZj?8uzpLBi-_>+=m%Lrwqh`sw)EreT?^6$` z1#*sBs2-8?)T3&#d_XN#%jH9AjanxctC!RU`Ive`y(5>Ych$S{dG)^fNUl_$s?X&b z^_BWszNo%Y-^iEMCQm@V;_2+^Du3_nBRR_=8p7&K}&qmL`RJP}5&(A8?dyV%R)y+HAJ5_b} zUhlm@?diSAdy~rZPWMh%J-l~&?^Zp%GrcoaFYkQseAV0gp!Xrw$Ggz`hzffj_byfa zy-#_cR(pG&_pVj@cwh9ssP^@K2XQHS_W^PQ#+^_}56Qx*Cq`zEXLzA3&b>TusZzI)UW zzB#`8)sem>z9s5tU%9Vb9pmrq@2w{I`}_N=WBmjC1JrT;LH?oYcz?P7BXy$xOaGVZ zWMLYoA^k&{uYZ^*5MJh&AIn>!4iJZlPGUU&QXI~IC+3Pjj`ic23w{sgg1?xQmog{2 zmpR#Q;Mdc*Z*sYZ?hsv=Nqqq`6Ftm-GiFj>C@f|vdO>7~m&B_qUl(t%Oz@v2J`ta? z{F47H!_TbdO^g=IS)O6&8OggF*+#Z#YV6BQ;Z2PK-sB_=X4mE|0T(ilcF6dv@mIE7 z&fM5tj2oEoy0dWyb6&SI?qt^M&c zb+WIqhWAVMHeNN}5IM%1#y6s+@hx*YXB(T%j?8To;5|Ys^?+-Dd{PJl==d z)9l5vk2##>zRdM(nq$nvLUtX4^IIHSZ;#nal!hnDfl}qN(|S`H<*gE-)WveWAHf z_{~SmM_FHNmau&ZGafZHmokG<5A#WL8SBrO&yr?^xq|iQ%;(ttJo8C6HD6@r=uC5i zxq+PDHs9tbWhU>OGv8t6=%D$z`8n$wd9#sWes6wHc{Z7wD9;b(X14sBHyZ`fcmE;H z7IO<*DtLF1VR?9aQB%unHQ^n2O|7QlD65$@ko7^wP zzu3B%oG-DiV*P4sE^j)%-@0EkNAu2S`GEDH$V2}=%5t&wnAp>L+LNyTsO1cv)&V@qQBo~rYSVII0X&9 zMfA0PWzOq%R)yt=W?Gwz9JIL-8PdbN*X^WF`h-NYHxVsmQ`waD*35TppxN8=E{l%L zac#;@vJ=}oGtc$OTGxw{(DgYYj6Tm5ePuW1xi-=2BSb5?AMZYDCr8SW;utwfjuIzm zd%*ipu?M1!wg;rVP9i_@Zg~&u_sV<45pt${OthAd%g04??8H*hUOp+G7TvHG%f+7Z zS^2EkU#^fVDAjYkf57 zB168+TZ#Pg72ZqKO}@&Ti43`(cN6*LYrLJvkQ?L%w!bc4C$~4`n;h{i-dN<9Z_6_F zz9ZjZJ;A$+4EdgXkM;NE2ci#l>_gEMOZExtpYj$X6PxxqX};u5Mt$U0@+;9nel5S| z-2Wl}!LPoN-?09j{El<{Co_U~#_nwt9k6_c$W^AYL?>EMf)3@K=EKx5F+dGh!&xs-1+4F<_G5jd8p%2~RqUf}DoboC%fonwty1IF;mkpf zO=aFVY%1%gtJB54>I`*;*c;1wme@<3tkhZmAu(q`< zr>H69aILzQUtx2_D0RKMUW`;Xs2fOR)vY8LBr)Et&`)%|QKR>iD8pdJ*x)I(~47=pE4D2A&?)FZsV{ZaKO>xXhN%zK2jW2Wq54o9pvqM_TRu`BiLv+u zpNfOjXX-Q2Rei2L7suisd?gCi*XnDw;3bG7wU;0~+Dl*=^aRDh+Dj0}X)i$>hL;c$ zqdj?^{_Mq95M8yez!G19?ejbjh_;>wJr9YFcnm98f6ntf>u-B_kEiw|(|!Z%_zl9+9s_&v7}$=-AX?%vJRy{Ksdp*wd&F-LIrt6F zv$vG_*+bq{-c{tenmO8=d0+6pz}_{?)85RxmYLc!v~R)tMZfWKFL=N8ekTm=VTcae z!(fSrA-ZV~gC!mYd+{(tbM0Y>OzmNi1`mVnco=NQ!w{zSEyQ5$Rj`g%!Ef;@grU6( zVQQ~}b-W7J2l)qyX8ysvtE-!Th<}L4@elP66?cn{57?_m$ud+@s6gN65y$()cu zd<7F3G z^#hu?en1=756Hw1cwDqGpTHY9$n^#?@dln|%QEvB(ZyVjPte5m3EH?mK_))I^Xy$| zu4M0Ob1lnt_y>mTA9OZf$3HM!|DXx}!Dp;%|DcKa1#g5fTra`GOV~s?f50cOT%Vwe z`77Rlg*Px5d#|m&i~^={b$ZO@b;Mw;dBjOBit?PW3{yz*V%Gib>Vu+|x_ zwQhs8{+Qo>g3Y$%XV`4Zwb`xY7uakIoBcKA)ONaw{8oO;ue7!HyVkm$YpqS)PQ{jJ zJ8igj+Hmc3$hFf0Tsyt5Yp46Wc6zYt$a_3OsxuZk?Lhq%A>7ywrj0hsH3sg z9n|sacxG+YHoJv7NuA6VZMpYyEq71Xa`$j8_g=2$?&(_Yy#Te$YSHTHWR>)L(?UHctY537esqpf&;nibD?t$5J2;$hc{2h}s`8T=J(%XteO zw)`;Hmgiy1UlQG2D_-bY@$Rk_&%=tZ7YD1?u;mA0%U>6HSn;<=nNSHa+O^{QxmLWN zYsH7VR(zOi#rvsG)F)VRZNCd#`+bP|R(&h>uVJ$@J(=PFPnIW3jBqXYp=s7S;9Bc| zC+>;!Ho66#g`$P$5zivg+OyKLiuKi=7g$eP>vwo>ih<4ED4OfGzA#+NZMc?Ox|aKJ z*IFBu)|ys6X{v2@bMM36hq2$w{IcT&rxkR@v`bWy`h7W|~#@S6XG-{p6-?b2r}&z8kRI(|prd5(8j+ zwYAQ0t+nM^Ys0nHmTRFc*Ftx4Ep%sp%pb#kYdh_C?KF2GaWI3JwL$BhHm;RnEnvNc z=R(gVo=ZJf_&WF|_>T2W^qt^u;cw}0@9*eehF$+F4B?llocCw5HG=eASw2ZLrF$nY!JF z4Wh+o6P^p2d1t)(Y%~&swQ%g|&*vT^nmPcA$gxqSpP^OR|gX zYP~G8WVZFH*80|KTE|-((CA~VZ{%2cfb|`by7AUVt=+B7TD!|uTD!|^t_Y5tw!)cL~%45*hYvly_CjS$)K9(nF9V}0xt@Nop8IAjeJOy35QJ$(WkMc~# zpFBr3Q!V7Vo|8Q%%kw>_dQO#-Jb(85Szh2d({rYbdM0}&%L_fTJon3syk2imUgq7; zd#s%9z1$m4fnk{RZ;PY)W!-?2y?-2$sw}mf0a=uDhPxvb&e9w*Oe&uFQClb6k|gbnUzsvj~wchZr>d!OR%tOm1f0g-5P^8v_o?tT}f^C8UmKniZA-dT?#-atoLGHNF zDS3>d2@VbJ$8v1&kkqe|`2>#$PGEZ^cv|)LATksDDY&uG<@xU3$!Te}q^&pp+uD0C z4_?i0uMZZnoF2S~SwzA*{Z22_!b=r1HB1D$i z@G>;M`$EpQ#xjKb)ZF5xSs!|f?d!9+YeJRl$u0FSvsz@etC=!S=hd$W3F^-t~P3b36e>8sY0Thf=qQ|Xh(?Y^S>YVPy~ zmstnuy;(;!mZtF>cHiD)sgfm39wn==x}4Qz&N?m&-yrLBtz)w;$hw4OENcqO8?&%S zS$Ae(U$W+Ap@*{;X{qb(UX83}S&8mkuAw>Xm{@M?nASiRIewuY~M1wRdxr?p&&G7=a$)_ zJvrahGCNzZXLqUIQn{C_luDDHes`}YOOh;Aos#s)-ivEk=~ZS^o5}U;gR{p|+SIbZ zUFRxQZK>Lu7s)$~`@PX+s7a_5SGY;`(R!I&ue;2iSbJ~wDK)obpP6l^wq#$JeHrDs zG8;RXeN*;rEN5iLQ+ty~$u7=b!1l%2OH$!>SAB14}ox4n~@6u&%uf}o?Q9D&- z*yiKvJhOkv(dX0X5@{{cXIgie`}wwQ&uNy^Mu-~A-0lHg<{VG0?R8T&v?VvM8NbcV zBOjLTdQOI3&q>=-nI@TX*DkmDD9KV)mou-8E)^vW!+@Nj?s|`!TS7BJarW+)GnPBi zUEkU==a5v&oYQj7;wi4^u}|_az5Z{yPkRFmQQoO=I9%B zZn>t;Eq!l8>q&1sZ$n-gN3Un8TW#svQ?;yH#Hsb_de3c<+m7_tC%5Ews+}fe*WQx$ zTUUPaR_9G8ht*o=B$v7UbGz&H2!ca6K{6?gfDyG*A1Z!f>sHZ=G9+;8;KE!+3*o$Ic58n?1GM z{Qt`P7C0TN?fPYS(Ne(sRGMI~o=0uX?2)TtL zNs?43M;Vv-f7g26=P@Qa^*iVM{=fg{|GuBK_IlR7K5Os2_T|}^ zkWNgg0y;4zBG{*W92HKPpYdT^b3o0(XK@O8-99I!L(1K7-yx+baC*uWLZcRkeBZV> zZuB!-_8Wv#+~rw*%ak^RUpmSOa7f2|_2v?laxR9;5}Xol@CF^x1D8=)$}$c&1^Np( zLvXI(ae^lZo-BBp;Msx~2wp5WOYm>?CGjH8*TnZ)!EZC>wBb!5&k#fAX}kH^Pjes* z9%aC$7*5&DoRn>VT`7BnJ^=cwl*3GO$Z06Sz-Lm9iHTBFb7;*`|64KAHHOn!9H$gA zCza7T{Nip((tgYJzXv1b3nl%0BuuIPyHEnxc7Yl`|~IMtoym&K~nt`N=->E;E+=% z3Z5c(2A=1pqNMq=k+E3_(SB)5(^kNCOWIU+nTAT0)-ZKDKT2y4x;5j}y{TV- z7Wa69d{U339tS>^rUSlj8@NexLw-V9dC-$MmYAEF9B+)CC-kCXoYiUTL)*$}$zj@R zr_}>r(w5dFtp#XFTUw{IE`fWPJpujF1_D2vHay^$NE_ZR#>r269Q1EZ+eS_k{B0Gy z1G1HGNxqi$bIs?#%|3Bc46`0iKgL|x9t6Ejs6%N-@%)p}XVN3!7oRaJKA)4AUWHwy zR~MWv_zJ=G8OK_XMnX3T!tv*h@Psxry#voq!|V#zqW{A(IY{^!jA5Sz`-wv37Q^FU zGa-F4u#7R`M(R=e0&yd)qF-x#%q1LdUxT}1qk#0c(>H@FUpjov5StI*lYRhvktcj` z`h7S4+?PM&En{JSL=0RJ?viTO0G2VP?+dsw$6K?R;8ud$3Wof_N19{vX?_n!LaMhOQ! zLulk3=&3@#BJ^LGwr*nD`kl~9=!=*(w{k3|r(lWQ^b~$=Dg%DL(249m(pu8PvQ?3; zo$h5=fwkYR>&=Z87Dm3dlRhd@uMrsPDSr2Bu9wKxtp=-%|m0{Y595m;h(myia zN@LneXFBqZ;PuRjyd`u6p(_eqTj)AWn-NT#ETMUAk1~y!R+5jppTs_0WXR=KMQ)G~ zstKLUwEa7#Z7C1dQKqe<%*T3c#@~t?)CIVyBz!LU%DPMN-OP#1XF9S#==+53!?ey7 zoWmTQD`}X*95tC~(~tS;&&;u+Vp~OU1Httroce4V(Zabx=qiGfh0{pz9fJQT?*AyZ zQYs=JF>PJTeCt}KZOL)_JD~?M-ySG*ROqPCl2f+i6!rzM-{#DZ97(_}C|Hf^V{yNm z`PMChhYRk3dzBUThjI9OnPV0Sep_s(a=ez*N2{mMQoo$COgm-Sm-D>P&oga*#kBnu z)7W1iSm;NEew1l5Q^Mz7figbl4&gWAH7`cWq;)ZKA}58DC${~CmO5)b68dtN-Lr02 zrk2oBicE^QZ^C@-F|BW8j9p+%Hxk>fr~d+9Jtdss%(0rE0XFTJqq<58Z)f+atN49V z=!rtN6x{tZR`{sSJ{DhPPGet}x|eA^=rq>;=*BJixJ(vXEiu~6!HQ|deHdGQ zKF$?dG`78+X?w6>DKoa@h_znae8@P`kg<7PQuV!r|GJd1GesQcF2=4cF5eW}wSqT` zufsy`5S$<>EW=oJ6HasCR1`WYsr^hi>Ed^maGEmKXM{gNbj4%hzBS@hkp>d4$R~om z9^m*guOMycU~xH8LVK8Ly;?YaJxUeMI>slLGxic0yH(h)o58eMB6zoiu$$>fBZ*N$ za27M|NIp8<*q0-E!+DO|K~GvSX8|ejB^+lDySL*w9A~@Wr4qt;q4R{68trTrj^vjk zdcYYkwnHSAtzx@^-`80wwB)I?SmJFZoJS;HX%n4|?AMYO)_Er8m(%c+&w*C{-$b872$RI`Bz9vBW!f4l?neoKvV`*|p+^f}By8RhdNlK~w@$E> znY!Y#F4L#hc-SLha)h5NSlR+pR_J?0uXblzi~LPn@q4$}N*~g+XO5Ddn7Tvww{cub za?N}$Y5QD!NsTkD#l7@Tl;lN(`>QH)MBFry5SogwCd|R!6v50_J(yOKo=8J+{~6ac zbB)kz`F-^nq5Yo5QVDGl=bBk0d^9YeUEyCTbX%cIOZeS|(^@#~8Ao1ZPGpJLb`yR@ zp}R6)Nh`0v5Po~{Ta8;T?6DBce5Z!cO*wYGl{s3kji4;Hc=v(<~Marrp~vwYm-c~JG8q!nGWLAgd(cO;iA0rs&9*IaD833RDsMzAMuu*qc?AlHfBLJGVKEdcLs`uzI z;9=U@i}9{rJ%-#z2YU5-a1edfE2HjJR0v#8B>-QossqZ&EcSE=>A2i)IR?dm=7p$FBTK7)JoQU?Y+)N6ny!M5P|fsZ^mSeJch zaPNV-;vhWeBhiU>z2Uvb^9TY^c*{*JI-~^bIx(-eXQF@ae`g&V_tXgtbhTN zd8k8*+os^hyq@5pIEFvw^#YG;OYmb}Z}3o%!yog|vsclOnR0-bw;3X8};gO_nQ5|qgpnev8sVp`J^pNYS0*NAw(4i?gRY=mGl3)Upp? z|Hj@*6DuvKw602il^Io5V}0Y29RV zR6uLMAiz|>`WnaZy`*c((v&?ZM^am+PEKplFu&ox>*%`lM(bPZmi0^gzi9ch|1FQ- zvi6++N3E*2>fLHts~xuvZ@v1qGPl*ctz(<+ZI<2MyWPsRgWE;grM7F;u4lWfb}QQ* zZEv^l-9Eql%-^96mG^V8@3RX%Z@o@`rm6HaO{3{FgPx(8G>c|qSLQeLEgho&qQmqZ z?sNK{j?xcwjQ&B#=|}pBPEaA8q*HX7&QK8=e5JH9*t};cTRF;W}H`X`kCfJktx&A`` z9lIL8)L-eZ^*8!keMtY8K5XtXcVlNrXLFC~V(v9vO*hls^e{b5e>234!Y=OC)@^pW z)y?W|_23;~R&VP*tB=*!%C+*WN3q9i1n=;&rdiXmH|rT|7ItMl%e%3#7wZK($*zXI z$CqON(q(oH>^x4j)9h!kqxcPbo&Bc09=ny^w*ShzmF$i7CVR8}u5-Cl+o|JR;aus| zb^17coqo>!PJibClu4WrfIrFk8w&emccCVKf<3QQRa4cJF2`=02~=C1(B-L)PQ-~8 zw_-PHCF~8|p?6Um+1c4n@7DXNy=jSknRjE4%$?NP^uq|cH}+)?#<>SsCX4!Dw_FSA zE4$+QTOF-V^Z<6k-Ae=cP3d90>7$fkjkhM!2x}7dn2*LzwWlfDT5K(*N9|H}BIVl? z?TIuId#IPvllCk2Yc$=t)TvIhoElCF&2iG4GdYN=YMwyOi`sP3l+>WI$Lx!7YA4>={H96`ro2U1(? zMCvN`56A2$;GB*H*m=7GyN}l6+@Gx`UhmR-P=1aX!buqM6w%4pJ-5;Nfi!l@9RuEM z{R8-2>p1Wh>qp@Cte=3lS|@=2W))&L;VSH%JBg?Fty93;t<%^~d%*g@I)f)4@34yC zZnd=&s@GU6tz95{FPC$vRWnX8vBFDPS`ob=a z@_V`6$i5XWs#-OyzuRR{o@?9J+pWP#vQn&r_C+Y)b?nCWZSa$7eQB3PX}`k0!EOT= z)vPq@E4v&@|CM$V`*yfUx4yQ^qb}67Z?xOuxu*4veKBgrRd!SCz;~@=tCsbxors!J z&%ViSkM!5!Sg~icv~@YB8B`goHm4PK7jYWFDHpmbZ(YG@g5AZO7T8s=uH-a;Q<39_ zS&8F>O=YVt#|5cyxH=YQRf$haC|Z1Kab@9rheLJ+yMcW(&U9#L{g++QzSeGu@NTgV z+m&Fpvc9t`BOJH_hCMLbXyNld((mmmcp7buK?pOgqjpvM8oLGP+13%9RdBW44D=j! zW!J}Cq-Gbuumfhlif+dqx*M^hyo2tIJ#hutL;e@+>V6kHxDQ~T_K(<&eLZ$z-)uTz z&-EkNWj!DJs+VEs+g8Z04CIAVp&*$GkVPdv_X7D|75P{Va=#QM;4vNP>R(K%V;tN~uNO3j7b}IB=m;2#h^y=+GD!<51Ve06@7OrWG*G!bnTGrCf~f+%hhYa~rx1 zf!n%mf$wlT0(W*h19x$|0C#n*9i;sgXY$ISs=<*ou=<075zIv445-*U0j z%l*my3HYRov7v{9$$%XXdeDpW5U+QUR~7hb?`q&C-i^RFdA9)H>a_uG=M4eQ@G^j3 z_g)8HjUAiH+v2?kyv^GM{H2HY#h?3rZ1NvEMlfq!jb2i`UZ`Ji_u_>h@;pB{U&UUwk+X*}M8hLHJKHfxceKaeYA6*wM zj4L0P5jQSwS=^y`JHCCX>ZNLz>Qt&`!*yBUQ5auodn86t8l^4)bPo5&?QD#; zH0Fv7a27@>+Ag4Szl`DfdW1a+~Xu1f+j)u&4BZ$1;XipSmKesig?2s*wM&7+5zX#ZNOvE=5h+# z0_P!24xut7qej<8&zN1^13Zp817|?O9QIwndFUx~*cCATC@%j#G=K&p2S(xCj{=%V zQ_wG*OABcUEvJ>Vjvf{%X3`^K4ujV4rJE^cE>T%RzD3Ed=i|ls)$G zEO0jc1vrZq02dI>UV*fJdCgR1fOAx7;0(p(y8t`CInU2|Q-u}}Zc*m#aSH7r+@jU93!JIIc`8YK#LA|s z0bZs`WqXyv_9*FR%UeB^q$bot)e%=$0_Ujuz~j^vz!|C@aDloCI8W6D9;UA5&{b{F zxhS;_;JYQH*a>@e`=C^0pcLlQ1mxQ^^eY$8Vp>M8(OP;NlGz4Hp=YT&L&B)->K-wN zVN@gKQQ;&AuI&na60Ys)4w$Iz>MoclpQ@9X!!WyL>|8wGDQ1@XotPt3OQb1NwFVxl z+5wMJw*rq;T<3DsEx_4|>)dd46L6N2nx#5`&Qmu74^yaL_Gr}#c!c6wo3FS|iw;m+ zjx*26v2M&!T>1-?lz7E8XSCuPlIv>=l__q90_Qy2#a)5JGHgJZ5 zZ=~{3-~u%UI8Tk0Jms9L0*z9v1god;_L*=o8D(KWiXHA~V_k?K(+YDTRw_U=qQp&tf9 z%M8D88=ziB>QJlHaxrt%D`F1Aw-!IISHm2EJXr-Z7w)(&XQ~&0hpJbBN2wQpN2-^A zbJTOd*=i;5IJE+JxLO39rPcr!sMmlWS6ugpDfDfSBa4AYsHMR9Y6apAfpau#m$AAHaJIf3xJ3J{S*PUa zdx6L4F2EVOGjM@s&620luE*N%3%5T%Yr}Ov_|4RVfJf;EfJf?qz&ZL4z+?4rk?07K zXqHIye(jO% zDI>MqR0F5LR0gMvsRTL?b3dHUWZ=;THP-EHN`oF@P;cF?hHH7gxdiko18rgFnkt~z zn2Nv{>)~|K%fUUTwKl2DF|~jTOd9YolMX!E)C7*@uW150)7%Jrp4QsjF1Eh|9%b4A z=a{y@8Kyn(aB~N6f$0F8hnYXF=jZ8XnLe=3G!H@!YYf-Z95WE~mxk;3Si|)(+i)Gs zFkC|m4A=Mbr3LjGeo^1y3#qrqnNh$QIQb2w*kl72n9;y_CP&i3E%tBH@aB*3ooOZl z4>ePPN0|x0BMsN|9P>DEwwVq*&iom8xS5Q29P=k&&rAW1GpO&#;YVScWo7~wn5Tgs zHwD1M%q-y1W<2l+^9)>eF;9ZdH%|bMF*CqfW%5DinrXo2w9;laIGKhuM}e6GJj~1m z9*tE!XY+EQ*#1R0C2F8q0&b>Z-H~lj`;oq-zy;<-;5_pJ@G$cd@EE_|o8_Rh4C{|P zj2Zn=U9mnGk0Q(}*k+p5z}bfNO_q5RxWKFh&NJ(PhhcRQr*aMO2%MA4Z~F%D81oiz ziB{Wu0A8lq37lhg0cV>JfeXwI;5_pY7~9@JH_@#a z<#eORFt-Hfr=Ud&1##hkRQ%Bae!hPnc?(akUy-AQ#< z{nf*Gk8x^}nt}INs#fA`>aFS{%t{|p$2IAwF0Yefv(YVed)*oHdjs`Q%v3+Fr|8*w zkzUTybDB$9m}(JHH-}Wqkh&$LT7}fDA=NshZVRb4A$5C5L4)AUIW}mN7zKS2qo8YI z6!c1rf}V*{9YYHGCdTU&Qg?;a-68e+km?*#_k>iJkh(Xdx`tG@km?>%D1otfdxli6 zkm?;$_k|SnU(83}kb>@u@t_f76m(#Wf(DFH(1C5k-kQx+He+a3;AvGkV z9uBETLMkJqhK1Ddkjf0Hp&>OQq@azFmUBuyr9)Q{T7t0DV+pj~ z|M$wi8%t5d@j@yZQgI;_A5x`4Dj}pwhg6x6x+tW|hE%zbDj!l8hg4!nRS2nyA%$Lo z$S&ek4yj8*s!B*z4XLD%suohoA$4g;RS&7lLaIhcrG!*!NTr2TdPvm_sahd*c}Ud` zsX8HbMMzy4QguV>s*tJ|Qdfsm{gApQq#A_OwIS6oq^=98Mj>^5NHqy5v>`D$Hx8*A zLaJ#<-4vr5{bFmAfm4BlwkB+8{^@sJ3W|5wt?sq-~~jeC8e0~|{m8ETlb8i>al><{00 zJ~*E}&nNo!2$^NfDbTtGdO&3W8gsP`u%rcFX7Gm&Lrx-Yd;z-{xPn!g?O=Z11%d_|t27OB_JW^Y3ei^D7VXKDC9bwL+cx%D%DoU*e% z&u`Bf`j_H6pDjTa=lUrg&(H06oltT2#a)-Q@mIhEu8X_mwl4UyT>bWgImKNS=kkgr z-$!v5XL_`{eJx+a>4Prye9rt@Hm_(ZF%zw0dAJ$C9el zx5-vBgFmekH`PkGnT?xLhM)c8>k5Z-X$e=u$yFVT|1lTN?k+3gt^;{kM{&*{;|F$` zQo?0A)*UiQ%(Q0ArkYv|G0pyeV(HbKbUUlNzOHBAiQ~tawZ_cwC2mXDn zK%y0lXa!qBj23%amC^|D*8|`zaKwd{66xT30|@WbUTU?UYrG;1--eN@iQ>{vQ950O zZ`7;d<2tORxE^aIZoq1Y8?oZyCi`Z)rQO1AhOhmKF2dK&hMs2LWxB@FI>S7f|J*-c-7OM)bw(G+!?!Y+DSO4>c)|0LXzwFDZCw$%i`KqVU z;Yz2OSnD*~nq$qi&aQX*g_TW^SRCI%1(nd$YKm|6yv`_sR4inkcE7CaIcNGF7yMQ$??Fs%U-5RADa5#oW~nQ_&>T9+PhB(if(o zy#%uBg#UyX)iIVvcrwkzSK?)~8s9OuL2G|Ohv_Hin|PI|l2t8L zA1l3DVVzeO)kjtvjKLbXsW@|bv04G0x>0Sz=z1S+Y&c?Ly$If^lQlg?^$4ksA=NFU zI)qf$7}X>6+XJfr{ZM*9Isp~V~ zy)9+OH(5^n%rUabe%R#L*!10VuBW1X`S0a>Z#4k3Dp_g_R+>!4yvjVa$bK%C*PTMD zV@P#~QOJ{!3T#m~VmwGLqyk$=NqC%_zAaO}EmMJQ5Hja*U>k(YwJ-3CoQQ=Ngv|Nn z+cM?bGUbQNITzRlAu}(q4MOJJ4E%D)3n6I^*Ec)HOrOsoGM~fqO`p$U@iT|yo6Oh3 z)mBGzxO8moWG$?=ZGyAhJLoRDj~=9l)fxn4e!g!Mx8 zB8?+0aXNZuoOnJkG8Ct$KOUJ9nH^aaSsq!7JFB+iUa5mP3BAyYSP59!RvoL>8latN zjd$yc8yf~&SysL^5pCBz+=cL(wH|9Tci}FAZ>(e18QaB*#VS~VSQl+`3-n6vwtLzG za0-65U0_ePXW9$xW%g>U(%ok7w!g3s+dnzliFXp6WUOMX?=*2*Vf9iMtkxam40pz0 z71LB_uCo|-{H${}J3E}c&R5P+=M+|vm2oR$b<-7CG1d%c{&#Y_yZzmVafrs(SFgv(c#hDXhC#RbXs(7bWwC! zbY*mXbaQljba(VX^qc5Wtgq5>(YUg4mE)?%)rzYZ*C?)8TCO#`TXI95*~J zH?AOVQry(InQ`;%bzvO~s2(A&V@P!isSY94HAX?BhE(7e8a3nvexXrAUf`F@tsgHm zYDfisp;1F#;1{JJ#)C!;slYEZYRC)xLZgPfz%SPeKVE3mkP7^E2;=2?7q|=jLZilb z(5N95_=QFdd4XSORN-;k7YRp`hgoslacK&~G4TZr=jG zft6QrnHTsC z26ARz;5U#n^8&wtoRQAZZy;yp1%3lLGcWKP$Qf@I`VHjFyufcDXXXWd135z$q2EBx z%nSSma%NuO7w;~41nGr-135D<@EgdPd0p){V!c@3>=AqJ7&E)Y%nmWrch8b#_bgZ6 z^xd;e{b%1j%afm3n!f3~XG!|czI&FU|LnVG3Gy?`&o_PdEIa?%ch7R;XO^08`tDg` z{EY%)=1Rp?1;xu(h%`X#W}ak>a^y_}o~p1FZm8#ay(;yqwSBSuxEyOcCJ( zU(Ipe17=yr14rz?u#5BBPebfj^=4xg0^Xl-v2fHVnd50geN?(?soJZ~Y5-=b%V0GX zR!J+H>Z*AAHS|WF#D9YQJfsE6epSo~_A8j9TWqfYegX5)@t8k2g!#K;_R^UBi->K4 ze5K9o*v^MJa8DZKNscYNsO6hVg_y@?^=|ygS{P)%WGI|ehhOk zjgc#T@Xc%@=3Q3cmal#42u6pAIvwM=|A>a^5kuMuYJ^U zF@kF3baDn@)HD<4FeGE|N&!YSuX$U&gHggbre3sdv}bf^bYgTtbZzvb=#jXnls}$_ z)ksG_q>|_wV1-#jo-G~);5k}<#@e^(AU3^YHasgD_`ynTJhhJ5;CoPL!(Re4=3KG6 z3o(_)%76Z1=a+a6_1t^1}I zP&h=T@um(+1!P%3(}bs;rMf-1OW`g9>)+X(c4laN({N_MWlygsc0>5SQ^Cte_}JA9 zS9oIw`!TSCG!bUN>5aPFNcGkc$JZH#Qq>WMW8l5l>?(r$*_)`nP80B&2oJln@JhTN zJdUQI#Ll4)F~j#UR?NJM6+dI?6Wkp4EowOKWLK*(^V&kaV>X*DYNOd5Nm1`ca3hzl zfZ6OseT|i9U9WH8I~#Q`zPC{Kwtr{$)c5gyfI8FOYQL|C+PmzJbQa3lK|R|3+CHqu z;SRu`^aN+5GeQ5EZ#2`-;YPEmdJ*4crk6U;I5YK27`e~YFFOmIWqP@@+*zgH#4Tp; z>P>D>x2OJq?;X=S+z;FxdZ)X~#hvQzC+;WuWA{_{Q@tA_^(6fXN^!E@DB;rdH2%gfSVd852h`fErD-%xcP z6!W8Ih|V>S*nQ0(?E7(pTW`!u46^&!gPm3O1K8zu+{M$5s;zg_Z3gM{ zUVi0%A#z7fySwxJT|Y$KuEX7Zn=tQ= zn_ty<+?ID(J#C$`PU)WZe0#p`6hHi?z?)q`=0xrUg7R^cj{L$JG5KB=I(L#=vD4#?g8w}tL9bH>%2?7 z6#b@`=B4QkUQMr-eh2qKUZFSfeUSQHuc6maZ{b@Z^?Q6DBu*6>>J8I>^G0|h^!wgO zZ=~MtjrQ{O2i`bu97-{6#zQH_&3Guq8@vrD#rUHXnyw{N*y+IKO{sbK9@Yso>(>VCDsm7zJ(S%d#=DkFCErDh7GK$c zo^1#&{MgXT%GdPQ#W+xlwyRi2^WA*n$~uPD@0fK=p246d^_Ws`-MJvj_xW3{ot@>opTJEA;;Wm$PlMqLeS!dSas|dSp z5#wVWmt|YFS%~jY9mVEm-zIX1Y`%dva>z_D6CfS-#jP0Ph-|~1zT5o2?D-xkfiOEmX~qPag6&AnGN_de0w`$cmf7R`N}H8(m)(A-9`<~GoV z?uRDAn%mf{xsAh`+qkT`jmMhXL|Jp2IM&=Io;9~A#hTlcVa;vIvgXF=Y|z}MJZo-q zF>7v<2q|-4fmaH&WO)Nxfn#HpNjrS^>}QQct;n%*sG_ydT1b`Tj^4`9?DymAH@;-! z`)h0QXNP;Oo8kOGu0iWir>6QQTwKF3@s}=e$AAk{;zBLLmypNR6z~Uv4-@=bB?Qzg z(+R$?3RSqRC5*0IvrJR)g@Wtbz&Ge^qrVNCbl3<5n>G?o18}kPURDTz2LE;msXX@e zDSaF^#J9F9`Y2|`-b+@}D(uKpSck*mv&JykE4mzg z9)KmmnWyU zBmP|_B-SAsxe0wHX=ogy2_}bC9e>s;Rf^H9N6-T_%it4h0PI;v5$9}aE6{4_JA#vl z9^_o?qxZ*Uqd~s_f38`EKBqro8-+Cn}^%y(6eeTI4 zNK+in30Q`5%3tY~e5H@bS9%L6>VWiO97&#hrH_}d^kwBMeYtbK&vOp=>FrK+{-ddl z{?^q5lFn+fj-V#e7^IVPA~?~2*FD5?qIeA~krU{h#awBziNwO;Cu$$E{&Gx zQS_|O4dHwzI@FPqYsSF!g;s~0jpJ-+2}tiH*5x>#rjMk!v~?*?b?I_8KGg!Zfp5eq zD4TKm$rhY?vK6PDY{MBRxEmb*9o9~Kar@Bv$od%n-MAlo4{itFi(A3>S^KR6xDosd z+y{Qp`qKK!`WoNmz7-yGzKYSmWPb?vhaa}SvjX#b>wD{{btJ%NQ;9aWjr9Zi3IDK; zTR&PqStqPQ>m>S!&i7ui^DWoz5m5&SLNwjF#O^z5h|XUAhzPy)_FDTDJ)%Hl+m z@;J35QFzQL7o&g4z5>n*sbp8i2_n8(1z#Pj;?$2AMk?FmEDLu5|GRkW%&skl??p0Ich;Lt4aOrv-XF&kKjj+A?2`dOjlr3bPF|*vze~Lx~($QoX=~a7Fd^7fm-6c zmdbPszHV2iR(wti-HLNs(&#pv(^3nq^I2J>Qkn2nm)Pko;i*!=iBbw~_$(N#gOxL1 z(yUs1LKDtss>LTV;XJ1MiiOx8tKYt+f9VV_oKJJ1Gq{F?x`oRapKV3NXIqsO2_=h! zYKVkVq~BTxrK&XA!%MA8=}PFKOQ|ltz<0q6gB>dm(%;n?cmpnxMA$Jx>EWog0!FQs zFm|nmb9A~=s_u@l>y0=|=PBxi^K%-}gE&E_InBW+^ba%-W6o51nQwKc<&hSV4zv~P z5604`k?D~c^f}+&PJfTQ5qX17M?Q#rKxg=Vb}GVpgo7xqUq`-CIIS{rL>bGoJQcx7 zJJpnh5$O(PV}w~zxmXL(Mn(C4a}~#9MOBJNhAII!m@iPJc}%FvxgWc`ReA3+FGVF{ zbXQYV;t`#yjC;&;)Fs{+Z;Yzyz2U7>NjzFp$$W#ky0lpSq4)5g%s)ui{x$hlEJa-B zt5~&h-dSI8`AnzkPIc$r5-yUgD{vax{oWw^zriiSE&A(u+`q&;=X-hx>Fk0t zzXnDd(}Uh8^nmvvJrs@5KzAZN=(eXFKl3{n=^R`1p3UZ9n(p2JIWVwH0{VPrd)WUj z=8owLK`q*kuLl=~=P66<*TcFuPo@iwiAn@_9a=Fl5) zTWM|dIIToJuZ(u4HBszc1IE1Dbm2d#;`qH6fz6wS9ZW&QV{=)2Yl?3da-G4rC|3uXs(Tna; z8qC;M1x<9UL(c$QmMO&h^i@uMxDLUfyDLO3S%%=h8gctK0eI0tph5Q4!n7(ii zLMP!FC-seUABKHJ*#8nHLn-5BXx?4P`kKi5bthk z5cv0=q&x7PvyMDFIBoXV!CdUY$@y(iG~>6q|EJ;qwRm{Z>XcI)>;pJm_cuY&mfz<7 z7sKCgA1Hbr;qA9K(hjFI3qkM>n-SkPew@yiG|pLc{`liQ zq5J^izoh(V;4TAkFyJf(F@xuSi&-@1pM@DIuj)lgQ&Y2-ZVqT~1T}?xIG@Ie$@GO_ z7UU>UG|8R=n1p9Qv_SapID@ILGn4w-vtT!?C>M|i_j6-@#`&fnhF(L@0ygP2MUOF< z%0<&)E{nlaFkg+?=Yh@#EX1>;|B9LUHAQ3enltagobS7lu;$@;8{o;a!2F`etl?sA zi?l5IkpaAHKn{T2Z?hgjnyLZpm|3l8j65^XhpMr(9Eba-%;2IIOe$@Pd`pdRZ_!}0 zowmf%akqJf--+hHT`mCWEXw7+f{7NLHdHhj(4PIVAK?=%WW|1w>)>}PRte8u6v z8AtR(%8#Fqu(*z|pekw!)9VNQ$jXTuy0US6aN!Vo|hAq_uWVBe+aEtu0{{Mj%^fR|_eogNi97&C+C ze{PPlzeGE0)BZ>eRX0+B+C`E9HK-ld5Vi9ffv!OJ37_q*lV|3YfTHhhuBS0@YS9)) zwpUv(Ucb}oD z?ppM92GKP46t!`mDSFraxM+jdnwrFUMejxz7Hx{=6}=x_TC~kO0Xm^*lXvewcO&5; zOz7N4+=WG7vgzDI^_=U_zI6d~M!&lcwTvF2whTz)DyI|DI1rEz_{vW!ZUo%V?~Zev zXJO2+Kk8xp5Qq6ahD+F_6W$M$75#ut#Z{zJ?g`5Ba?x*WNuS4GL>=OL!+#ddifavX zAk9V}a)10~v;_1luQF}(D&egB=V=Ve+I;6QwZx4)S@sar*H>v1(v<%{0rq6tWF0~| zxrefGj`|pZo9PM2X^h5#0TTh-CQJeE8yW)L)y}yS(3RR@9%GYt3GDs=yQY9|WAybfGZ6N1 z8sZ$JF}VLg^pJBAZ3KU#-5qYPf*Y9LWq=)^8-mx1?r^g~PoY{2(dLxr9Y;7tG{(c- zGngBA5M^ysG#zdd=?0`>k#n4~F+XuL*Asggwdb*z^hc8@9eK4PGJ%@dFC*VRraxd6 z<89D~cR{a?jQ<$A$d6EIN z(Q8j%V!miY{3wJ`f!?(T7rhwFi#eCjEbD6Y-QJ+L7?6flu$yLYr_ETY+|@pVbaqAA zI!d!a|ApO03Nd!!dd}|h=~=)u$nzzqsagUspx?*pO6Hvc{1tEn@J@_ocdI~e0L%rP z2An{8^Ud|puNkzHP2704)7nS_<0jHT&;vm~#?Nd<(p7RKod(0a$p`9$XNI0^M(5L0 z=ejw^t^jGTyhM9<-w`I~UcGU6*E@B-S<dHLsV&S7ka=6IcCH`!Gw2-RGk_mbhl_UN zd)f%~HNu{Pz8vC+`~mKtq%1%O6NhJ)Zr5Assz@g2>#4R~o$5rA=sKn&m65LhqCRFA z;(Hc-%6DjBWFN}oTpAf!OT8oQ&`*04&ui&^{TaOqXad*@NR50-iv;%2TmMg`6{qZ@ z%yo%eOZ_9iqaKldsAt#Uy>FvV=I&UNk8|jZeHN*Wle_|fZ12p`f z@Ec8~F44i%#aj+@H1)uEW1wA!I(fV4=IBk-B@XMd;^L`?SA`D6y+DT&>cX5)ZQ@o^ z+qjnjeW^|K2dv^4s?#8c!PZc<$aJC;_HC*igVhWAESu{BO|tH#9Wb{6KG54SE_|6D z0W5=?U3NKIk5$K>iBb;K&v2sCpAen+nCN6OQQ;eaEfg)>Np9g8z#)o1Rh6v555PYP zHx;SWDUYHjyO4Ey4JDi$Lh&b0!R{MzPp1;WTSxJQH^a?6@OLHn+x0hC3&#UoS$hh+Ev0CBJwJeZk|jI4+YG%Y1UIf9zDI&CzU? zn6OSIR)rbRFuQ}-9lY)V9nIG50BJF3$9~E+_5HF5cP{~6W{97w5&;RIe^uBe!}6A@ z6mO|EaW~K;hG+wt5JemCv*(J`G|*4cGRiFpanor0Ik1k7#W6IcFG9%1v0Dt|>4yNK z2UK%+ovP*oJs#H-_5i$3(*R0!r^BVXgMSs~2eRq4==YF;4;2H`>!m8jwS*ghy`Fwx zDBX&Z0lkS%Atz7uk3BQ*{|D&J@BaS-aQ}_RT)`MF0rN=C5%lXS(f8=XEx?VdGWG*K z7k$22Xdhn@`!SfO_hCGb9bvy2&ws;ZwEH7>(;5e3H?+kaor`G##t(DjTcV9^itk9J zDL>&>(0@YzcNcB|<&B}Qao<&{*Ndi?st)&^sadoi`kLG)1q5T<$Yq4}I44_? zeX3FdO6p8)FH91bjOyP1o9GA@9n#+9{5fGazid z)|n!GkWQIW=+DJr%|4y{8s<{y_*A4}4f<2LII+A5+KLW1$@>}Io>y|6>W6;PyBPE4 zBHpL*Tn*_sf%v!Mc{|d(12nf6hZqp=2AF#QyD?|{Ny1=i@7XlReUJJgK85i|CXY|7 z1nPsl$$%^uKu&$!i|I}7r^`5i#{vzIN19$nc=^b;LDpt^DH28Btt-Y6adb209iEMp z(xnkj2F7Rk7{@)uV>*5ZfwD9wpwZV`hWDGrCi5T&#eR{;8{lU>=~GDanYNLRGyw1Z z9-BM{41f|&wdS!Ery2SAIm+N%HqjT|g!Dg+FrU!tX$fRg32kw*Q;z;@MX9NFX%foS z`&9bWLk-Oho~76~4!0Y2@41CbxQ@oXLhaoHDI4_W<=Su?W#M8*@{@gFaEok@06=41V-waUJur8~x9@kBb(N`eO zWk}b{i1P)&2t4ORFQLf{;Aa3teU3m*sk4e%g%kRSg}^~CcT-wpH|o#Z;c1^nA#nStlAc*af8C$U5GDt&t8ak71?8t>dCuy)`|q>(Rq@E1H2eFj?P6L-|>EMB8!^&5*gwPMC}P zJq`D|abLDD9_8T^^V;bikO#_W;pcHnXtQ2}x>t>o?2VYK#y2zQr>)SD%kUj`6w1|P z(WjN@sMj5O@LpOP-yb&}-$CQxzcOTNmrB9>RcE?|%*nU71|e+dS^k~E^NIBWhVzVn zg^q8YLisu|n*nlvj`c6kIWst`>GsHQbvd^q&=E^eKTcDrlbc=45po_t7K`JO=ym(| U)XZH$ukzd_1Bc6VncU+3AG^MDrvLx| literal 0 HcmV?d00001 diff --git a/src/qt/res/fonts/Inter-Regular.otf b/src/qt/res/fonts/Inter-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..bdd2c66286356601363d335afc34aea90b3ac9f2 GIT binary patch literal 258288 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 z z#m*M&dG{bO%P-ywXRh+CRjusw;eyizrugJSzdRO}-M*QyUg6ATKDop%kA!8Hx^OI< zxk5b=6e9+S&Da>2>8^1Hy2btBpxBR9?+rlGaiT|;h)8Lb<{QaG7Lr9GAi*2gE!k=?^eAUO1*ap1*L?l@7uSoD@6ZfmdEqGky<)4-dgW*6=IBQu*UT4dYtn7|L zCUkl0G5KAH&5Ve6^eWMPnP`lOsr$t_;!3eG-z^56Lk~xSVc*uUzc(bS?w9@{H%3K7 z=E&B! zzD#yT#cH|m*~r#NUoarveqMY-+#o*9SNbO|SH^gp-%ZH(d}5&=I@cxEC~AS}YtqTk z#L*MSMu(0*E;9>cZkfdY8Z;&{ruklaZTn6b*bQYOTp)5!iVTqv%W=ZTBVwK3ots|f z;J*hWppn@6UpU~F!@Yt#&MKL$h>xAPu=oBPu?%isN!sSoaz6aox&m<$g9-I{^K0OF zPQ=N@>UKM`oEZ|Wn- zq30t(=tENM65H@R*#IXa=&W%DI>qU55bCZ>U{Gv3DMlULjxpa0iTVDZT&T>zLhK_K zE-C1f*j{FT7Cx-^=g6cy6bn895aCP|6 zaU1#ib%9_&E`^vxbJiJmgiYek}1;LOTo(F5b*~u<&vcSvrAof7HUTk!;#*d#pb7IWdoL)~P?dWfS zTRS#fPM9ah4Hk1|hQuH;+akcd|uTa-dj(;WaT(v_xf|Q+yV{ z4L>Qf@qc3|lLPUl7&uLQ9yuX0MVLlumth$me*mL&`>_NKD03mkDTk|Th{|nGhTaXF zTz>NT)A=Gxh7Zd%(b+P~C$f*RYud5h;<6XS z#g7aYKVBfl%J0ZE`En&T`E9vG-XiNofw*!;RL=Fwy^o8Z${FIb0vN1u{+Z4wCri#b zAsXDU=y32~89w65N@tW0y1ddI1%aAVCL7?*J6!yC0v~Y&kVIHw5dJJs0WG`}TKu6! z57%R&C^PY`y+@Ah-uuowyT4gi_pNUxuzgu5GOB>1az)m0Q79nPCuA-Nl2InZfH#Bo zZQs7{wb#ZEA3l2g=;7nH?~8)&qlY&Woc(v?Vc+4`4jcf=-Jnoz0T>$=d7?8GTn#IT z4Gx0A3-wKK6Jm~&fJNv5vdM+Z&Fvqv(x2WE#{Z8Ag51A5+}GpFxi=zah5fm!1L@pw z`Xis{_2Yv-*S8uy+<^grefLL#2L{0uiJv=%eCg>f+}8gFLi)1(@}saU3^_CUR`^8m znH3QGbl8PWaMzH3gdcO(1fPmHOXRW8mzeD8fbGO=(Qsnt`q;{F zx(gOGCm^Q*$sqvP_xkeXTcQ(1NAKKw=8j!3cPT5T5R!*cFlG>74!? zUohOaJ0N$+n`F2A5Pvs`Zm}a8_9b9|`?dsvfYnD}SL!H`@ZgkZfccRTku_lVcwKoZd);zj&_TKvH|vA&Zh z{my>*zL+2n22XF_clh1$`-aMl@xx8P4CqO@=nevoCuOnd4MxQhKb(Ybt(+5(lY$U1 zC)0U9cy#Q(VME6b4+0JpL-L&p@vL}Q{C!bufmkkEAY#@1=vPd++QH>L2L(X zhex{>NCS4Ury$3fm-U$3 zcxzNXdyCvNN`96vcVH7OVk_K$t}uMo)R6PGb36VHFNonkcZzen`*wl*mU}z?a1H-C zce=MbZxx7HneIe4-~^lri(<~iMUKz$JJ%LC6P@$iO!vA)G50%*Tp#|fD+r2Qr#mE9 ztb`+*D?Tl7>VOJQIfbz|1CAwo#3}b%?zyrDp5gtOQHZ$cS}D$j`g9kFtsl#Ea$T%B zAeU6((^hOV3|ZD@Mst20>CYQfzkRsKcqtNfvJZzuiy!uUuR^yu@INcW1kkJnp+^QR zdg960(ts#>@+go_WUEML{HOx^g};Hy?yiv#o+4z7PAn zb*UfW7Gl~?(Q^2tD9T5eWdXA;#9sqA(GeAs{37G{0Z={r`0%0eu_Iw(Goo&bbbYXt zB3}&r`OD(Gs2DEi`XG98wGcjWhZrsfBTUcDKhy2zhQu!SQr}&vmIoUO04d}jO5{BV zK*BKy*Yof`-SGH-i#RIV5tZdoih!`yU(O|xK7m#Mh@ zBdl$f*n(iF7>@OjJOW6$LT+=iVIvSJgn<$P=yPzjvM={cZr`B3T!e3x{n@Zf@R*w; zIezg=VCoAJ|6AH32f^eLuzjQ{i=sEko|h0nKt)H0%`p{V%Iu!Vp#L=?5lT1oC&HNs zppK|$@yww!2iqk{xZE!Im#MunJnjvaq>`sYawzv0!j_siHsEt`%4hu zfl-+ekORl#W1%b>j~fG_=4-_jpFrZY*kk^uf0>K_d^;kDJKy&O5ve*CMS!ojiR)y&{OLcy_8$M~?I%FLFx*WB zROgWUS;)!66Ae)KpfezXNVph!ipUnWY?k~ec=Yi212SXS*x~VLj*}m8XSlNv!R&T& z0qTL&Zjgn;VC*rnn-TVpz-&DV+Cns>g%$kN4X9A{Y$zN=e1%n<0>qqwu(A_co+}GC z1(C;^0QCiSg}8S}7ikTDM+ib%B(&hykb*ci$O(YrV3(8OmivOr3}jac_?&R&79i}5 zm3}z=S+K&HLB!7h`kA{`;?da~>>m|QPx4LoqxG+YH(v-jfGzULU4D04zvh!#S_>lY zkq(P4zq7DE> )A!}~-= z!GV{i#!timZiam{PDOzoFax>b*uyaekgdh-ahX-ne#3^N!vLjaaoKA!Tp&AOfOF-s z#WCkp=Krgqa-4zj&`DA3yYk;6X9`wwRs+QMl47E~P!z`C4&t)>G=eVQA1;$Q&tNIy z!iaqMFLI)|P{v~NTp9N{4x)*QhPA&M8FNndUF_R4^eI^&hvXxp5OvQJH-tugdOJ)l zE_f-25B@g2V^1{LSJWMHE{(_;d-BBMh#MDE<;y~jj!uyczUGL#x^F`0(Fm|a0(sgM zkgfbk&NTc3#TkOk%p&owSRpILRq{u_jvp^|{6hXl-XZ@w z8#yh%=vanSYPOi7T>0e)^(MpQH9|q>#o&c{M8Yrjh*o&^d*to%D&W$U@*!Y~9Pyo5 zF_pFX>*Hdxyu%p+bqK>@|Mz6pwGsEEc-sv-T@zxr$Zul#EA}{njp8@rdw+HY4@Q0~ zz2E&PbmG8ta7M#8&DsxN5%Zx&CZe)3S zjY>+0@e!Z=@$W#Nn0UQFq~UMIizmcwGG7$PU%VcZGj{sqH@DXA78m5hfE1n(W5i9z zkpI{M!_yMkDau1b>t8!@{I%^uTOB*FRAjH+uoi|Y0P)WF9lY3jF%Iu7iS@0MU2?)~ z(I1h=5b_iT`*Hj3aJtCSv{8{w1{Wtj>-4o$UEd_6%$+@ z$w3Y_tsy-S`l=0MpU9%5wf~u1OlzY-pX5|ocv{XV0Bo^f*2l=AcZZ2FlVhUgRsg=~ z&rckkA2-LUYD)X|lhdQJbEZ#@dJ>^-rkGnG=EJAug2Lgd6LQq^F`0bcCo|Sgd|^-i z(Ra6>7TK?jY+emU&5XdWt5iTYG7K5vtZ+zfkrT>p9UI$+q~Z%FMdCFvuE4oZG`i=x z6^mkFaI3}d?iRn34stuI#HUW7vp*`b0)XXvopG)kO<#nBM99s!Q+`WsbbSTV0UBQ= z-iy6}Tz&VTbQj_P$VyVo5#N;~5y&>Ki;31v;=+%_=zOt2-U97^C)%HdB2u{TP{`?a z<_AR0$E7G!A+G|z8`8NUH~R(P+^x>60wmq` z0`l*Ui8%phH&UZV2JU6@H$x)gxtRcv$ah3@rUwp>FH<<6+wFz7UWdfTjuRq@oZf1=*N-&P2#EedxuHZ1 zJ2ol`CCKy1>JzZP!^DY#htr7iw@FnRIS82|pbQc1zmv8@;>S;m%U}VYE)e)2r^p{a4Le_rykPj+`2b{~qCi$p46!yOd-uo21!1odH zZJ2?f;}{z0_S#7B7m={@h}aql2WI%)LbuQtPA41}NinD@{(Rx{H9sgre+tZU0pOS_ z0Q@`RAdC`kIg4YWr+*EK2%AC1x$=8JCnw*JAjR3^x7;G%BYyEn31Z1Mxv{{#SpH1@ z&ArLJMJ$%@IH7ql=XR7zADQQq*WDn$;a(zNE`T;zVzKx;kZB9Dck{)o;#@aJUV}1& z{9vuvDJMGT6gXAlSK^1_Q}Ka(QY@99uY~V&wx-J!Es$32NxL%x?lKp-;YlJ9a4rN{ zC(1kJ4-u6NLYDI(c_k8aop(d3QH}xfgtdPqBCM0h*S!#1>hAY1?#m6yt}?g=Sscqor!-&>Lb8BZh-{>2#NgZ4osJ z-V6Iws&v#!zte%(MttTBI1Sr#@1_Vm?s@RV56R`x{zU%_U(TTa`$qvEe9{^%WA*aN z0x0iTpzODQ6LEv`$MPrcr4ys_njh4DA?$o-k=*iiK#>#nuTkcsAFB5b$TM1QxD|<$ z3Y9oH5Re-;0kDo1@8qXf$c1cTi-32fV3j$93S#W5ra|@nOwMpyr;S(># zP{;ofF4wxBeABn6jqOBRzr52Zy z^1-v>p<)!A!M9bBEgUFT!Ddk09dU}UjsOVFk%_SpfL%l(IVebWg`6FffKTTIK8vKc zoausB3(FDxtB}zOP765`#0uwk*O6gc-X) zTp%vT-?zBB>p$s(r+^qht2(?P(wIZEXa{mc~yV`4-mOiL#0`Zfe7 zS-{ds{AIyu@4%mGlWLe{xM-oqG64iG24TZ8Z@7{(IL&vl#-zx2b8Hprm&k36x|b3J z@YlOxftSf+ZZ-;wmRJa>@5At}gOtJwF~bK_J5D946ZvpCBjB%B;cpB$HiO0)c;cuE zs7^P_?4j){89p$IYMQ0Y!4~-QZWy|~5-t4n7Hp~;)!)6?RJYg?6AhVSW8`aC!`;dd zL&1rwVTDw}HAE#6XPi?Qa8$;A6}0FOXtK&35pxTj27kD3Rc5*ux5Z{Gt`PQR7RZqY zETih8Es^vNXIW_I*ecvA6chBtl$YMZ|wDID9)I0=4Ri~r+@vzs|7DU zHGAIj*e22Kd%Sb(ifm%+)WJhm~gwG#`j7opDsFanPBjGPL`qL`EQYp&MAin}sdq=CRL-?|*0NI$iKo%=x z7jmDMieFxV67^`GsJiuF>yUg%NFj_)59%QU-VwDoMuW}~SX5*#I_$QTbDI$mWgLe4@N zb`p?o2Y}lTy;~*9-OX-KzT1O*_h!tuinJIH2t4R4%*Wsj;8BBL?s6Uo)$AO3`lK2X zdF!ELM_SSTydNM3KJuhD&t*f#x7TAHRsIe8Mk&OM<;ht%VslDv!vPsT2HvqfB3qDP zaI(L8b^Jb+RT*hUbJCe`a(U_8^g#I;j$^zZ>o0~l!eWV@kV>gQ?|VVtUG7|WK4`q# zT>^{(ZbSJ2NY3Aeg5u{0yS~IE7p%aQFH+)kB!pzi|HbjE9Kf8u?;=&_pYRo$38&ZN zif;Q~-uiO^)b*SOi$YLBUgGDU@F6cn0$TnCkY)5XtTy#Wbhgw$hD2(*n zd^z8QKo67+QJgSex4esFlBn~^`+@If1u$I0@#C^dw4l~LbSS)1SRO2Z zd)kj6HbVk0qvqL(UAeNu=_qhI!R!v`PZ4~Pv(b6l+2}qESEaf1*?C%JB3)cleOMMv z-B*2Ij%ZJ5PWk&w(~qT#(@Ry3(%srW$S0%Z*6Ey4Gbr!? z3O@PJ<@wPw<8W-JQ>;WWeqlg-7;)$HS;&JrT>+Gbvq9exa6d^r+6y0trHdWz5+tfn zo9hJ>L8)_=8-}~{%l*y@9FKyD>-EcSHxD*ti{HtHEogBI;lFahs4b|tA`jgF>Th*A zo%M2;Smv&Y$u0gIJ_7aMFaa+OkN^h{5HzT={Q6*ehToZiuw@RCK8J85D=Eh;kBN&S zIIsef;Qs zFhmRSHVzd$f~=KTu>hs|&0=E#@)rZ;8u?jlS^$7wl^52?=Pv_z8TXK~DhuFo_982( ziCG7bEBP8+RLS!5BVw1oKP$aCospg)vLfne77nwe_l6*&+xnETdOWad(lyIQ7hraX zFmeeqpb~|mCnm~+eOtuAkh?CN2{)lUCOlB)Gk#}DSdR6f7}+z+@>cv4sgqN_c3zd77j=yN{5Gj<9TM{sGrspAoF12 z7}>ZO9V5d@jbqOK5R!K{;BOI*N20~qE427Siyp2c;Q3z=Jtf=SZp2qP;?u~J0hE^K zieqlE{0#hF;;sO%|F!>5fB(DvC;Sf%uBfiwf5pFIeai;@k88pF(~;;uR334?i-6TB z$N83CcfC_Cd);z(z5mQAXAms@xR4m(zf)X%wfNZ`V%=jOTD1ORoB;f7LnAq`e8=b!)#=HsL2ii+Y)IdJT8DC#Mg3gkde z0JepkeDNGkorRqt80rN#TSjI6cSgixv;7C0Q$Ez_dvNfCYCA*6mO<0QV~>jrN@91x z)L8(?OQ`JH6U_-YF?ox)QofFuG~g|=aG&1S~VjWIuSX$ zB3Kc)8XV?VMP^u{N|bkU&kQ<~i#=7Qv(qT>z(nskEVO;k&wO}obf4((t-Q5;EmGIO z9bG^msKG0{_kXPM1Rb0LD-5-#d(|l)(a_)POOF`z^={C~R6rRAozlf;mi6O59}Y%2 z>(Yl5cwEYn>GA?cZ4igWu{pU zPHT<{oSCIqKR1xhlygtsHLRyUQ{t!y(xg=)7&&_$VHAR2&_Y^q)Mci}moA?kbuLyB z@dU9EmphTVEq`9lKP-CkacKlR&N|?OF^Hb7Ukl*o!n?k-{rk%|#TFv+!nsSC7zRrQ zS9B5$(yS*TLY=Z>7#bWn`5Jx!P6B>G0VXn0bOEGIeRzDq&|%2FDi5*3pH>I9#h+20 z^UXz6e#+nX9zsq^sm#Iwhf{$w?}2yfAliBbb+T5!yGP*YEDrzp%x0kaQL+JNgK-X_ zGMGkvCWMkyA9SG)Nf>p6@FU-}01ktEgaPEZnX0Cf?Gs_(XeS#dz{dqtz~Ms({u#wV z$k|~r3vtG?Z`G2BKWCp`+~q9rA=1uG_ez{jp8-HO!`Td^6;x@33{e=`vvd2tJ!5x{ z96omJ$dNn8?!z&<^CGGa`6Yr1-s%^>g0eO_GhXCl7MI#@VH9v@E^$R7k`A&dUMmB6DK!~Y{r>LXdqyE27LdBV1d{h z3Chg+<0noYRv~TX;j&?6^;G~eIew=IAajK~Ck9HoT}UEqaYvv+c&g8Z6o=u@dipX0 z{hysVg``n1DvO7okmUt{OnF#EIpXt&eS3nAFY@IH!=UKwJE$o6v>Zj0TqA5yGB|Pg z?AbTqm{(xwlbaT=EfB@WN6Imf@$&KV|6}dV<7>LU{_*!E_ue>(D@cgs+zgUR5E3K= zK@bE%Dlx}YT56~vsWD1x3KC-wQ>igjYp7P#6s0vZDk@6RQbVay6g}tOb8q^6?{lNo zPoL-W{PFv}NV3k}`|PvNK6_Ye?X})3sDlOtvNPWRGWhHuiC@vd@-Qnq*W8P0q)5)q@7X7T3l? zorvi$S5dJ#C~4|Fqmt&hN4vRu$`}(5DiK3|oceykzo5#_y4)VX`=+Zc&gY-FLge;oKlY$wXWa@3Dw}WftB%b;9 zcHHK$ZgN!hw+c~7oDHiR>{JoPIg6?5eKynVaZvHzwr%d*_53F0mBV^1=trYNj^-k6 z4Mk#?aGao(Chem+1`;SN^Jtr{As&8e+1WS`0X>9hD@T&uqRAgc+j9p!$Z-jiL#k4k z8Y)aYZN+wKi4V~Pt<;l;9(GS!J%v)@lj$=tDv0d&N&{r=HmA0Nr6uG@z+EZC*Ydea>OR%t{K=grYN=cmc_2K)SzuHW=)}P9m!Kw4DI+@)90}}%Yby5=hZ_s0DVZKL z%zAqC!d0$iWGEiqy|#@rD3(u4|v!d$`?NB6xq~Mc6;EV{$*Y-xItBT zkWeykrixWYc}dH2E>eM?B2-Vl<_;Vqo~Em| z%|b0>p}j~Hd;776uJ_MVuF*+G8>#Gdyv;exWn~tX(Kl%JMyCvZ%OpxB8qI}Y7})c# z$3!i|IQ8iMxySvLom|#sGi?_+!AkdeTrw-(>v>$uHN)HE>G5+exu2E_9&R+25~oor zN4Wf4tdbsFC8eez+oJrEha{&}zx9&M*j4)PL zd~XTs6%X-1R~tQioEd!a43j10>oQG6Im6rvPYkH=v^?maiCRDy5CJp++5laE-oOxG z7%(1~2D}I4084>&z{kK*;0$mPxB>hM`~iajhFUN*fFTZs4lwkBVFV1*V90`D84O!s z*a5>)7|y`(9Sk>N_zead%K4#OU6hMMxeh3ogmObr?hMM6pxpN;cN^s%p}Ya*y->a; z%EzF52bAxF^5ao{0m`pL`8_EA8Ok3+`684*kMb8${zsI*i}Fw4Rt0XcaEpUmJlxvB zEgf!?;PxKevf;J@ZXdvHCyXI5)`qb@jE!KN4C5>q7s9v_#(Ws}!gvVALKwe+@g|Hv z!}y49WH9~-lRHe6Ve)}VhA9E2E->|ksXt7w!ITcuESMI;ln2vBn0CVSDNKi8IswyJ zn7)I#Cd>_BUJUbIm_LL0ILzO`d=2JbVEzN{2Dn?`?hp5HxJSS}2JS84-UjX+;XVTH z8E{_$_Z4uzi3)X5AqEw?p+X;27>WvGP+>AEEI@_DsBj*V2c&9{f+5v~6ay(9QfEj# zA@zqe9nuy^UqkvH($A1=@G!$80UrI~F%BNn;jsZ8Kf~i8c-HG_gl83a2Ewx*JY(V6 z9-h77IRc&&;Q1ar=fQIYJU@Wv9(W#uXE8i)z*9rTYN%*M#hR#C3l$rnVn6 z_$Df*qvA|dT!@N!sJI6e524~oR6LK0*HH0iRD6tz&r#71iv)`WmH=2HV2OjJD=Y(H z84Jr?Sl)*vAC^yGIS$JuSgyfx1C~3mJc7lBO2McUfl7^0DIS#)Q7IXf`l8ZMRGN!Q zOHgSIDs4ffov8E~Djh?m?@{SFyZ|p5UX9_^23|wpl>x7J;FSfhP4GGluP@+L1g~Ov zeGjiE@M3uzyqu_PMrAKlu7S!Ss9YbFJEQX3sJt1K^HKRDRQ?Q=kD>C{sQeu&|BT8{ zP}zYh^-!e)s&q${o~SYiRYsu7+o-Y-RhFX43RGE*D(g{Y2daFEDo0V}460m2l^dvX zA61^AN-3(=Mb+0)bp)zrpz1rQnuV%MQT20FJ&LL)P_+nE&!g%!RK1I;4^Z_9syg6p zhIbWsTj3oJ??&)$3GcS>?h5Zd@E!{9x8eORytCoG4&EQb`vAO);C&t5_u>5d|JRK5k4vK833O*;WHIJGvKodKHK0^0H3emQv#oxsAfVnZ&dR|wOXiF z7u6c0S~FB@iE5otZ7!aU`D4^$tB>TjZYI;v-)`W#eW zgz9;yz8%#+f$uBuoeSUN@I49NV)%ZK8Z}X)E^5S}MoZL4M2$C5BONu~LybA`GsAB( z{HDQg8T{75Z!7$E!S4Y4PQdRQ_yhiR;QuE4N5g+S{3pWyUHC76|8n@RhyOPC?}7h8 z_|N@x)0XFu%3eT0<1s4dJooTbWw%%PXxFlAP@nu2xyLgcm!l1;2i|aLBL`J ztVY0Q1bmEu0|+>dfU^h;Lf|V1j7DHf1a?JWe*}(3;Cl$nM&JqrZb9Ha1l~a4Z3O;? zKovpd5afv<9|Q#=s2_rI5tN6Z9}sjCK@VufgJ224GJ+!!+zi3(5d0>BGY~u-!HW^R z2Ekhqya&OD5PTBBml1pm!M`E+Pt>e{n%<}xh?;d!vps4iqh^299FCgrpk@|oE<;ES zgaja@HbSBi(i9;H2BDC6%bk#p&!sJj<+ zKSSN4sCx=^zeU}PsCyN4e?mlUL_{N^H6jKh;&nu1B4Q^ZiV<-I5w{TW8|Zpf&wzR! zs8=zTQGN25JxbP$ccM5A+PbQz6)LZka=^c0O0L>mxoM6`rx3!d8avRW8k)R~CR5Pl12ox(Ca2KEiP#9l_Cf6H zh#iC2DTtkg*oBB)j@WgG-HO=#i2VYwClUJyP0OKaMKrCBrZv$t0!=4L5t34F&i!N(c(w6xQ7-`(c(|El+e-#Ed$Xq3N542vISZupk*RjCZXjD zv|Nvt`DnQZEf1jO7ijr4T9%;Y1GIdOR^`#E3R=l%RST^m(W)_8C7@MTwCask!_aCR zT1`c(S!k7mRx8kIJz5<=tK(>O3au`o)lIZ|fL2e?N<(}C;=3Te7vcvaek9^2Abtko zXCpob@hcI(7V*y!Z%1n*TDM2*y=Z+Htxuu#1+@MVt$#u5XK3v}8#CHeL7VDmQxk3K zp-n8>v_+fAXfqRSa?oZa+MGk1610`jwgK7>MB6oJyA^HspzR^FJ&CsG(e^45>LMW$ z3DHRCi-eI#$VI{`B%DUOW@y(6?b@T=9<-OyJ{;{EqkUVnPe%LxXg>n&Gtqt_+S|~< z6CJGR&;T9Up+g^Z7>y2d(P1Mx>_TF7B+5vvjl^gqE=S^OB<@Dy8FchQM;~--jE+sw z@l|xpM#mC#{2d+tK&NWxBS9g;dDX$q3QMwfEv5`-?Z(X|P>et@pq(Dh?<-G^=_bX$RLtI_R!blZk*d(iC= zx_yOi#preg-EN`VQzVy1vL}+QNUn?II3#yLa(^Vhh2+^tUWepuNZyU)V@NJS@()P< z8Ocxa>N|LKCc1l}yAQetqWf@k-;eHBkkS?@i;%JkDO-@T8z~2o@+DHvA?16dc0y`b zr1nPYYe*f9)QL!)fzN%vc@J*yXK;urM(9lh=7QyzUh(We^v1fowk`ZPeF zX6VxqeR`wMVDuS*KI!O_i9WN?CmVg;(DyC$9gn^f(f3{SU4XtT(02p+Zb#ow(DyL< zo<-ju(DyF-s_17%zZ&RQ7yaVUuPge!j(*e7ZxQ;fLBEgCZ$J8diL_cs`v_@!kye1T zVx&Dp|48(YM*oiJ-xdA4qyJF!AA$bc(Elt3xM4sr2E<@MCk%K41IA;(bPSk@0V^?J zKL*^x02>B+U|Dg&!)q~kZ312^z-#w0WC?~G#*ptZxyCfG3-YSKaDq2@#ad5coQS8V`K_OmS9wOj4H(QioE_skVSF;i_rdsq7@vpnYcPHz#^+=FV~qa;BEW?z~G35-V ze2*!&Fy$epD41%()M}Vo6H~)6wGpN^#ng6~+67ZnF||LY4#Cvnm^uMdXJP6*O#K*B zKgHC~G4(d4KETxHm}*BRkXZqlb&%NT0Mn8&Z5XDF!?Y=wHWSnGFfAX`PGg!4@A%`L z5WG_t?~KMfCotU~)4OB(U`+oWGrTZkK4zT5yZ(6h6}+2^cYnsa&+%SOyqAdg#$sj* z%&LM}Ihb_>v#w%xZOrb9*$*(sA9F%6ryk}s#++7|lZZL*V$OWb*@-!yVa_qkIfFUZ zFy|KL0&^>1ZWYY+$J|iNt%tcyFn28GF2mfln7b8ocVTV;<{rb`Gnjh;SxLz1fvi2q zI)JP%kaY%GO#A^^cailNSt{lkFt0M^`C(oN=0#v$4Cb}OybhQ*9rLm2=V1N{%wLcB`Ix^4^FPP@<5&=f z1qoQN0Sof6U=J1?z=C5~a0Ux5V8JykxQzu5vA}_BH)MMtI}F*8$Zm}67RXLOc4uU# zBD){5hah_-veS`071=Y9y#U!CBl|OCA3^q4$o>Y|-y!=svP+R;LQW;*_#!6=IpN5O zLQX7l;*pbxoMhzmM$RDQyosE7$XSM*b;#LQKOyHA}#Iw+eErA-6hm zH(|Mi<$hQmfaNu@JOaz-WBFn%{{YMPV)+3qKZfO}vHT}2{}s!hVRe)@;X`U0CxO*4)LK->}vbYX@T8RIHngb!+i{CA{AP z?~lOy#aLep>!)G;RcvUE4GXd1V{ABr4QH_7CN?V?*p!4#eX!{bY#NVE z)3Ip*HsxW{7Hry!O-HclYizoVO}DVQJT_OtW`At1h0P7Hxj8ns!{%49#T{F!VoMUX z^v0Gquw@*!Ov9FW*s>g3KERd_v1LED9LJV(*m4tF9%9Q=Z21#gD`Tq_TWe!$47Rq$ z)-KrE2V393)^u!Lfvuacbq}^4#nvKhy@IXxu(cH1Jh06d+iGE3BW#PuwyxOL7u$wo zTL!khhi!|nZ5_7lz_tQx`wH8>#kS|jFNgd}$d{2Hj{Iok$0NTB^7|nF4dib`{x0Mn zME*(SmmvQqZ7UVZ(>b@!>jrScsj?u(K6*j=|1!?3|39v$69QcK(i!y5pmh z_~<))^anmR;Nz+I_!M?^$FA40YYcYH#;(QKwHLds*qw{rzhci|?3J;1CiZ4w@3;7* z20j^pPtIUpee7$7eY>#lH1_?3PYw9gf={dB(>C}t51;;mPyfJY{`hPm_IqM~L+pPG z`&AT_LxCp>s-qwf1uap~9tB-dkcxu7C>V%>p(q%Jf>9_Ki-L3%yn}+dC|H7mU`-s1!oja`@EQ(2#KAvsC>)1c<4`9Y zdKHI;;m~#*I*UV3aM%lneQ`JlhvRU#4GvGo;dMB?9fuF$@Ch6~i^JdH@O2#i1xJ7* zzBm$vBTaFnEsk`;k#0DWfg>w%WGjw1anu7xTi|FH9PN*z={UL$g%%Y0pio9(Z4`D! zVG0UIp)do5@1k%a3U{OMFbdD0@B#{dz%hRu3&XMcIJO+eR^iwld{G%+Ou`q(al9su z*TwNj9B+o>U*SYeoLGr3eeh*GzMO+E7vW1@Zo!uy<12T3<%zGt@Kr;6^$xzehLe#v z*##%p;^cjtio~fYI5iii7UI+joLY-hzv8qHPRHYPB2K@G(|vIIHJl!a(-}COhtnHz z`V3BggEIj*Qxj+E;Y6aOMEMZjP_F;_F@bn)EmL`Xau*iXtP5!cnvwML(nH z8H(&UTMlO>ob|=oAe;@y+4?xU1!q6O+5I?Mf^UFtBzz;|n?QV18{aI(xe7S!h7fbP7cYN0a-}T0Kui-ltmzv{J0xosMrGB_H z2AAH$r8T&;510LLIT)84<8o(QeifHT1*H7TatGICkKSkoFxAD_T{Im`~ZNyJo@zZ(StcshhakB?*j>gSJ zxVarSPvhnT+& z9v(HtqqcZ75RWF|(T8|+3y%}D9p>~%aV#4{V7zk%l`@kbl{@d*@9C{3YsfYJxbEGRpn zoPp{KwL8?^P)nfx0o!Y^6~G<<`&igFLi2{!AKG4MPvEEv$7nbf!Eq7}1&&fU8^Jjn z&Kx+uMX3d)YfxH%(uerdf7_0_E zn8DD%VCY~lbTb&%84N!g%J~_}^)i$jX()HXQ0|hU+#^GI4?}rBL-}Y!`GJP=>4x&z zhVttSRNqi(l%djiL#644O0x}>o*F7?1}_-A8X3IO4PLVhUO5J@n}*6Q z43)yX&~fWRf8|sd>xByfBitBvD?w zEgMJAAo)NN}1-qt{Wyz?HNPl#N0K%(g#4R~B| zW$Du2U}d8DlVr2|AXhDEvgvUTSIe_xQ^9Cgs(9TbNq!dH)Rh%ZP_V9&BdecP$$0f2 zNyF)T5nh%QE1Qa!yDEs1P1kZ=r09@2k|SDv=H_ay-Y=y|_8=c&))uV}7j~|$Pm(}T ziH7=ZrFpcqiWzF3->mT5n%OvFms;2&lqh(WQ zEh|wpiW}+`Ipg&N`dUUc`MaUw%awge=L7nF{}b<0Y07)#3T{5aJ*AyvPPFo#XuAt} zRlv6hqYl~}Ye$^djLIaFa>RDcC`{9Ykq7e~2V~mI#5_|u%W;6YXr z9b{XUPN<=>Ju6sJa@6o@blRqmR!5009okwSI&kT@jr==-2)yfTvDIi&{<`$H&HHWD zizr;Gx9;j~D^P}tn#E%*%IVI$rCk}afRdYKC5B~4v=;qppn#~*-nyyH8!!EB(=$%o zqWD|Iea?8>ZLSog9B#HEE);?)v3qmCi(DF~-{Tr^n#ct&N~%M(JRc5`hsrkNn49-A z9|`k8|Ah7{HW0&Mmu&n^@n~~fbKj{os6jNYe#$!9E%(u0C)i>I1e4mrDN_Yt!3bk$}}H(U-;EjrYaZwXjd)FecTTe%zbY^apz7e?wnF=l114Q zCs}NxiRx**L8O*T^a3d}_X(w`oXykpZ_k-Sf7=n+Xo{I39B7mUtdCU@OtL~YUyHWKZt{tfGD)@zSY9r~keZz)4#LQ7Qi?BMrnQGMv2a9Kx zB|Mu-kRd&RUGuy+OnC%IdrS)wxwfIo&<1*|Pt6wBkCfCgdirqn z&>)?drEcya3fOuJ3@I1TWuEl^0pwEDRiq>(NfWuUvk|mAE?dmvwLGKq2@m#_O(sEe zV1B3M8Ev!aUftfL^tUA$2@N(@`;DfZHUdGtK|0K&?dP}B(wvD#?RQ(EN%^ESlz^em zBx8(?wtlRBEiKw;xkV2K`wVANX%zoPITK6A(ooE;geCYrmBH#_`cXO}w0kjb4;J4xp5b;{lZ0nm(HYW8?3f>?Yl{rCNaxu8*N`J$`yYk+1A;MM43^J zSq-de3)?7OXhn|Uu~y}wO%{d1wURi|wf`7JWuu0Jg}zbM=36cNL(B%34f)9h3kXwo zipGb+hTLAHq)TQ|faPr7W3AD2;u7yjt&pYw`p3Cm;HtrZv zrWGd^pFHFDm&5w}&hK^0OmTOt)Y8;glNMA{i)*e8yk}K*N?MthyYf`|kgh7b6`w$5 zG#!&V3YTPJ_|iak{?d6%<}YmAqhC5)8wjI3Pdj1}Zg*J>unM19#KrMZhb=-ih^=RS*Cp$C`J3PjKl zZyuzEI%&wNo-k$cm8bBPxAB!*nTS*Yt!1n>SQ$tDL~>#KDy^m4Ry9tc9U))WOfHD8 zJBlxvtXZ0CVci3j<^-ah@`xaGqU66Zgjt#Xd?i(OFA1}XrqD58l0zjyPL(UjL3*OL z)EU&&MREI_^@@x1lHDJK=p_AOC7GMdLnXT&KO_HCJ7OBDrP3{E^(Es!VOxht{Mvd! zdzO;?-$2t{rPO;ij+y@*mfcl~yHlP|W7o6mNw%H(%28H99>tQdvX~2{yN5_(CHcFHXmw4PT-o?G zzX)C8ed%A3L9=YTC#Q|4SrZsfL9`IvKx@_@q#`~zx;w(v>&#oF~v)yxVS}ou&t0Ohs4ri zJ8xs9v~cCh@}h06)f;Kn(w#a}CK{AR7A5z4Iy8yq@WNK8vtzCn@ml3Ic`?0BqHLVb zh`HJ&iW4M(Y}m^!BNV3K!(?u|f)RVUiS{U0mOb0Wim|`atd`P=btL{eYZU~s^V~N~ zmr5tnN87b{`F4~_d-IF+?n~DXQAB&@SRAew<|EvT>$pg+b;Th%d7VuPvpS~RX0V1R z$&tWoS9!9CG`C{qb)*ZVLSK4}th&=Rr)FoO?@Mh&bNC!Difcix+Hc6x@K<#5T|Os9 zryRSPpQMu$nb<(DSMzc#<~?&eym&9ODx6r@3_8&4jA5CxM1!(>>S=`gc1J_2v%F0m zD>!nx0S^O1Sw3 zR_zw|Bt3y{hZPcD3>WQ^?6%mId|6L+jhUDGqmES8Nse^8sGJR-C7U8*t&Zb%my!4s zWirPE%1H^EK`>H@v(#q)0q21ute^`))1TH<;Q`K90m z&97P$I6wXs)PT+yVv4Kt?2eBmrza=48f?$~cRB9LobGBx`;33*ITn8;sV(fgSwV!D z`^sqBjenQw{P$9zOtwAvSZek6N+4QqE9;qPtE;z4Y{~zwgbOhji&-}OM;39}yYpM? zz4p?_)rNg2X~XPmv!v@?bb_PA$2qB^yV#D~>N|C|+iBj&)l3OV?2t!T@#9bRcliqy^HJpV6PH=g| zie_50$kwi3YeeJ3m0C`3xlZ15G#kk2$ixSFlXdd8nj2wVCO@N^L|oeo^;Zz2?U2rG zYmqhzjv(BX=3%tcU#PT?SxZhfYKdkg`72|of0;{#Eyql+AP*xO!FIn$xVIlIZD1Ok?v2#*q^d`HS@>P$#+gaM=~4vru0nU8Gp~1f@OD z76(g$ay)jNAfLl$r-@ObWy!XE6B#c+HqkX~7M=6OK5lxj-fVx&i6t=E*%o4d5IWne zMTh;%QBv&rdL8D!>JYm*r}%ntXMgc3{Uvp{SUm(~TP#)vLD@Hm)k08IH*twlD--d0 zmUylyQGi?*2xeiHsMfa6IFao${e7?AKNkD!CXyO=TT*Mk!pHCzyo$jM z;#0S(I~W_e4S| zU!WjfQXFbRuB#QHvhl$aon$kOr~Vc!%kI~c%0x?%5Kvptkc zxcwJdn#63S*q&QoviFfI+~d_B#muzYp34QE|UW_c35T$A>5%h7Bjc)~8?d|L*h}M#;43uO$ zK{^<6gKp^#he{vXD0O!r-Sa5-CeSyZ9%h%-48kmIP)Z2$N>4J&p>5Kk?Ghc*jP4;; zQUl3Z1X&O1e6{Y2`ta`j0=>a0FRNH~V-GlWz>eF~76u8&p{DmBAmDkSJjxJXDNh-|P#x87vA zf~Sw8NJ&i5!s+Dg-Baso%uVgKxU;`AQI`Pd+PvAMsnjAUQ6rT*f%4z zMfU@ot4araBOUlOYc}x%CpdQ*l_ohp$}rn>qf$Nb3$3yi!7cNQJ zw%zJLv$8~cqFr+kBx0XQ=|tGKPTC?t)AKJyxu*6JE_PaXZ6(2ddK#6rKT`wHM>*_A zl(jTvpCdECkyx7hjill#Jpxk++TlijT}PmWqB|w1e<4Pf5?_TN9J2)UOve9$9Bnz# z)+&A+mu=i{NN8(qNnzF#FCq+mpq--Y>}b{V&5>FI-&WRTdyQ($b^`ZjD_{GalnBD} zUtQTn)y_COSpz|IXK1OWf^v+qaZeM(a9QCwgP;m*89o96X16#)IAn69o%Csblz!4U zdhUUKceH(j^7@OrqpXYMXRbRfCsgXy;iC-LL{EN6$@ZtPmq-DxH$7Rinrjh50=1zj zbXiUzm+Ivp)c6e|9_Nu`Nn*iC%I!@8`C7}I$WDcpT<;vRnG(=+&mJL*%7Ba7b3SLq&EL=6f$bh{P6Ib6s%X7snc zuBZ#hD$F7|X4k=r%U?Oxv z=OIW@9j3E1B<(|Wjz*!bM-}@y4?l>3C9nmwS%D%x96dAL_tU>TC_s5ECcN<3xA~e} zO^BwBmLSi2XE&<8ejkQ!PJWCb2RU&tFHj z?c8gO79`NE&H6W!ZMRSQjF$c*YM-kfU-`LE+CP!qx$JLdKp%nN=6qaw&Gna#)v*fk z{7eN_0^jGF9of~iqJKxfP>T2&c<|GW-KhT|AeA{ooUn?tmClkNtG#I|_kRAO4|f+y zC){+!zKhgV`+~HO6{E+M=nxa9jFkWI<|LXe6%Uh^+1Y9QtL?Pk3$_+38?B13Lyxwm zjW9VDlJ8VFLq)Q;=4Cp^=!9m+V%s6%Y_1%XScg(0nhnjlB<7oKl1xkm=M9ec8^rar zdtXqbTdRH@QsZkP_xHZ54SJQ%$>VD{Qk}ED(r;w6H!DCD zQtD;Vc9EtM^q!MNPt)hWervq=+=AjVn8c0>md(ksDa}Xw(WI71WUW4- zS+DEeZ3fkKpCegF!0H`UC|>xOWL;xc;{vZ<^!a7VBQLZ=?0kL+w|(Np9Yv+dUr8H! zSuOlgZ&R%tVkJd6+dU~rP<*EfJRc%u4rSBF1!{;nlsKw$bckep2_inbF)#j*Rp{I0z@9adw0$H{e=58Pw$m7y_G**+oRP< zcOAKb_6M>l^QBr|G*1EXl?avjK#lgdXmx8-Xy&hFvMGpCGbyErLnKtv$rF6Fzt89Y z%H^?pyi1A}<;x0{o!IMzXq*tg+*KPVXi~0DMw0*gZ`x`7&dajNJxnj|5bCfou1Z8| z68XMAqA971XJ*LeNrPM@^=gB9xSC5OkMPm1RAMCr#pt9)#vwUaC)I0mh2$5DwqB1r z^ITOvb;b*&xJXrR$Q6=aNLV6a4J)hTs+Z77K@Ie$X%vZ)8Ff$GaE)CS%v44wO*LsP zf&@%P#Ws>X%dCeg-QlWtx@_{Lq$)}c)#I3c>uNTogCA!6k9WHYXhDwLRjzkQWk-aQ zWY|Cx55+c6!5tdbFK{}czh;zXnH-s_dkVpZGL^MgDJy#hXc$Wt29>HNxq*0s!<#oHmDOfW3YKz3SUwTVX{1SO ztD(JCMXS_CBHv47pR9@I*YsYi%g|nF;`R?^qnc7id_O}FggMJwCF;}i`F`Qn+)Vbh z(q2+Mi80)pmGn*_!g6#giJ zGQgmcF9l8{1YjSD%=CIDMIj;A%*;B3>IaY6lmeo)uDFoPJ$RtmPe=Taivr+ zY0dj{-D$3Dm)3Oal-t37oH$!Ktdt|@XD`h^P+R9D3?NNeGHLjdasK;vuKQoNruNBQ zn--|X*tZ?+_i@)Xoof)|j4WM!hDr#y?yk+&e0>~Ir7MkLzjs$+lr+B=Axrk|nltmG z0LvuVxY>TnM@i$MUmNX}23nJpz{@o?>wYa#fBcJJntz9nQ7UoXHWv|OX#KI6VAvwa zjyD4oZO|rb1A?{cT3q0SlIcI(QI=XAgS5zeEwE6l5Ev8`TPvkI7yMA)Pd~l%!~LI$ znX7mxu?b3=zxIvth{itz_-ad&i@N^j?hXv&z8_kb{J(e5!JBb`eBw2|dwBF|ddwx6 zJ*Ci^>M~bFk~29!xL}deM#=73uaNvf(unEe>nh6I*|WKw_#*-y699}m%2?OOCmvFg z8t3p_FIh5V(~Ot#$Ecf|;mn$-K$<0PBl=PFqg^!J*tK9Lk%R^jNhoE}Kz~Z336#0k z20FZ4qmhyV#}JPN!W}y&nny^Vh{&)MvxusqtfcjYYdo(POh%DbLQtzH#_H&8)U1Vy zMJun2JSrt7OdHdVQp8gI`5fZ5Xzfd%6Nig;{xr?~`t+mSn)pR_?oceMmZlh6*@Gx*o5;ficvcIo0HJls+>$_f`9qXsjH}FIQ1!HX<9hv#(fs zmZ=P4ODQpj5m70L-&^3zQ46jnbG6(_g^Y-7B~zY%S8JlZ&IoY1fsB}Vn0lZaFP{Ax z``zyoA0l!`{7kg}s{ZHB4Qoze)sv8HvjQ#lF0xTz{f62GnH_7LgIp3PakZlfc$`L= zF^)&sdBkLmZdJVd zR1;$aJS0;1Q$5%x!?~GCy==KY{~23|oxzAh0snP$+3GlgC|?Q)9|3e09wvz&=TN(E zur-`her;06Wu3*G8>JoNlz*2~zJ{3cwe=}KO}%Oycj@{&Kl>{)tX;b2uI(A9^iU@l zRqc^UQMBsH8(Le!BRXOnXBkb?w8Z+!?m06*4%C)AtNUmT9Db2E81iJc(z=H2P4%SD z{bpZKRITGDLQ=C44sJqqtb*7rb|Vs6hK*)5BzEaLl}MREDpV4M670S~)^MBpa)cMt zV@7znPLAuK(m=6(NifE;FgG2PsoHVXhd61K>+}u^W|SAlRM}Xf)OEeSH=&ze(VzIO z?Ou!^oVV@1Wzms`v5ZpPDweiQA$wW4j z1+UreH4t|=Cs73bmn0@CPqbbBZ)TjLlFPE&PJuK_tUtnI?K{BZh5v{VEJ__>*keW# zG%>C*Xg#!;2kDDNh}=(qu-Q#ChAj1;Fu zCHL%)|9`PS5_R!AQvdu1O-p(8k8fQz@>r(q%WjaI!<0Ez3xB8b_lFnk`Gf>LXDod{ z+%>noW+m^Tk>O0VJdVhv=Kl)~(-F!j%^i%QwmIZX)xsdFUa_W91xl7uz2ky<75L?Z z93o2MzyJIB6Z3bhETvnsGO7Jg{?WZ!TJ0ak9 z8uc>dx4c6omL}UmahHT9biK+Roh0j&`HG-g$;R6uf*`kJ_70ZfJ4lvzC0yD=eUyn* z8*98Pc={TObFTOSYyPKz22pBnf=a7y+*O_`gK72=rFcp&UD_M8kVL1fp?!ea(M_31 z=)65@1S2{J+ItY{o1Du~v-6IY&fo<(nupf1p{c}iB|}m??Hq(6b-mCD=v4`2f*_P@T_o{W<;FvE3XwvqMw(L` zSAz7$1kt{tLT}gYjfA#KFY}I2N(o9kG{BWYbpy>NT#cuav8k&rPqsqbdqKR5(ohtL zkwnD57gvA1tLrr#Vah6M_XwTnlPK9Dy0Ki5)wxJ{i=j)@TxFr;qIb#_;-jn@YcFJ) zsQ#9CjSE7{r8luizfrHv8O7KMoa#JXoMBQ6WHZPx4e3W-ZYCqMGbFpo8D)3laZ;#J zBiy7L?F=W{P6^g_hDy;ri3rCQQC_+4IzA?;_k&1VDY{V*v?&kA2+I!Y zs0r_P!Nl;R^&A&$#3L;<*}9avIjzo60byy2w#6H_krQCs*h3xlZW=32cRO4?r91{_!3jrO1cpaDKeLmINf;0LaCh8+i&r8tN z`NI6iV(asU_@y-Cv0HDk4CgB}gR=GE(j>;CLR~OnM@=HdTtcp&;>`I-(K;nq;4Ggo=@VXF>W93Vi8Gn7#*z_nb&3+N+^92&5Y zbFKM*8z&tSrj2bMXsgKk?8CSDTr>Yq<7JSVrHrO|S{jYI{#ztyHq9!Ur>v!aNQhNX z0b)Q2B35?iVl1f5%&a3Q9>R*1K&QkIBVoHH#*;|7>^Q-5{fo`1HJ)`_N)C}M{L%L5 z(th$~UMbbAmedheksPK=@**FkLad~SKrD0&q$ubLNnWJ&)|DEhPRhn-gGD9})P=oy zvSMXL{Pj{{6RV05W$4f%lmeufE7VXf6$7t@nq}5%>r}GwNA&iwU{moVb<|6$2DVo9 zRyVtv<+`IZLCF^9Yzi37`T8+E@WvdU*ke-T0P#MKL9%Jk4=)-Y{h6rJnV{GFR997q zGI_e{gp@*4BdSUSCF`mZQg?MtiAWOtP{xI^x5_$*l&jB3xM~!6r*stsDg9aOU+tkf zOmDVmRC6jfV)Sk#ui|JZo~I?s=4`5vNw#HLeUa;+O;mQI$UVxW#(FVdHnoAEXbR#P zYfb)A|NoCqYX3-=>X#C#o|dMHXCTjBJ(VFEhl{@wFKJ7({xl}+uQ9y!K=K=d6%TEh z|L-E`*+8qUi!JS+(WZ&O}{_VtTsu$Q|MD z(Ow|AYxR_t3@@lyi+J=q%a6*HfH4Geo@BDArJ1x4r%y(h7Hb^Wv+K0>{y)+-=}j#t zv1XtpgAseggno-w4TUcvOo(8UQWbT!mk5xAxp3&x!>sYLg z)GTw(*e*H4 z{i!dBX7qs+rNrv&&_c5LQo|Lhbdj7w#l<^qC3VT}m-4K+zeL3*S3hS}`y|S7`;RXo zd2^v`t`PGbC|8EhaJy#}}vFxFMXT}hNg zx~)X)7tTTQA4L>F#4XooC?702!`iVk!$fEPAF&)nZ-)KVn>p69-VAd!G)vS%!Nt{1 zQZL#m5?$>iRn|^YqMaf^v{Mw(+ezY|?Gz|6)Q3 zRrbY*;=79{&pgLS`c3f}p+x)@IQ6s?xQ~u3Cyk8UBr5WeR=3ITythXUCCAau$oLZ# zl=4~yCBa{WEWdQ}WFSMy7>5jOo6^?5d7rb&KE}PAwg2<1L;tLhH8qnQNfaplGE5RF znwNTmIyv!_L)tq^dm56g9$#aG7DG$^+ybL=>?5Tw?LTJvot6UjiT7e9yX}jqu@ls{ zvihK@PHH!7;bbI5jrU|*o{My3s5cazpopUR_DqFG4x#{yMo|T*2+ot&;vM6--80-u zH{lNr&7Z2Lpt{s4N=>_Jod!@3M6-ykiHzsvuZ{52)-j-YF9w?ZhQRfK%BnAT zLO|n=rh~PL#48sO9*AqH_&zVWs6=v>7npOgwa`N6UaA)HlkMxl*+*R9?5$1~BF=%~NPzG*_vo)zgM?Q4M6kxzTJF z)sh(|MvCUz|J2@Y0d*P7B;~3SouVZB zQ;|DLsjF;fI50=X9-i(uR#FcuJN=aVG*_raqny(;_ZaGkVCbwQY5*+GZc2%4W=to$ zyIJ{|+P(s&4eTsgoW15*MI_L9v_kP_T)F@)Y?GB?pXyFBW=)weZ(LbG2D`U%P1G`v z{LHhJMrDIO`rQK0iPx`3ySO6FsPYezSU!F zXOxNP+?{A}G$AKI3CLEeeW93tbg!!zmGD$C8-#Ydld_RfN~y{GhuSf?5#L}lGE?P^h8M*UhTdwK@m7r?MHHRA|EjJ!grO`@^N`69hbL&WJlrnYt z$bf^|aO0x&A1*uU|MU^#0|%CRYoZ4S-m+bzKcQpjWh-rHbd3ltaQONJjU~64DT=th z2zPR=q7u?4V5ruEQ*lM%*+u*Fwl1&npRqE^J`F#n)=>2uD2dPN_)J#my`z)K+~%ph z5i||)Rk5U!RH9^C5tmewA+nkmrYAbbvTUMNEvc4hwS?A|#E&ybNb!P*o^8$RD_QcC zYSJv(bb_%R8MBewjib;x*7cV->Pg&QW|$l_bJ3D%B9tk5QHi<`u!cM34BKvHt;vz4 z#rY_Ac|>L#YZgy%(9^Guc!EQ>6d*zMI&EV}JQcFNNQe`ny(j`DK1EMCA&g)`mB!re zUplEITcL5di;~4+!x44-ZQml2NBymf5SORyla$K;kGl5(kD|!>{i8eGGmP07*Gx9d z^vpzw5;Y4^qC|)iB}!D3C|QXTg_S5MtceltxH@jJ_v)#j z{%ET9b{fwgCvd)Zqn0}Xp93c3mEZl)a?$v?>mbOHPdtlCpN0rEe*uIN_QPD4!H8<(G1CZ2NYKUseSRNJ}@<7?lFZ^C2XxG zKX*t)8_v{i*cX?mJ>k}7wCPr5vt}Q{v4g4sOqXi+Myp#UtQuay_E`bRPE8PRG{I7& zj|62AFQ$iv7YrPtqX~+wJcg*BpuKcRDz%L$_FtWvMoD_Sbaf7D=fP5-65Ri!J(h=P^h#KR`@q^~{ zk7TsnA9Xm{6`c|!q|fZI0SResmX1`Ji}@zIw#<2QetZb)M(K!Cop38DdG58JY_J)kW(Xm>ZbTPp-oiCZ$5?Alzx0t=UHo}vL~ zljB2)PQZ|ztVy-PhlnpzyOKG~8O$uleWT<~1;A@04~Lw^s9Y_ENxe$E9_77?MMEgL z#FPi^CgVlF`?cL-ICtQI)>(@$B|A5(X#*_zbzrhDxJ>KnDe ztq8+#->2S|Bb#FCc}TJ6{qn|E@fkYP*M#K|bwIrBRz8YraWx4QOuyRuh`NJg^Oi7F z!vyHP&t`Eb%N8F$Ajae9ZEIM4h%E9ZwQp)HLtZBa0E`zU{a94r5L>g6Wq^Hd!v|?S zt_rilQj660$sCy@7N~u0sSB+Lb=jtHN41#Db#Cb#&RtM>#lSFg!m`6nhj44>m+lt2 z9c~U%cJ@Y98k_8v;!6s71yKC6McM!Jsv7X}b8b*RBHH@_#PH{x6P4ddUq6qnv0UTa zR@s~TMcp=%P(HZYuIU#|z^7W?JL+6uSC9hYBoEbt-`b7btie9P*-`r$xwKz&HLR;+ zN`LlCa!tSJ5-%pD?`Qv9=Keo_ISZ6Cm^$y}oVH}nsljA}r*)co$5fQ%tp7Ngdd;o~ z8-^|BKvHkp6(@;92Q~H5#~vK#DWay{{n)cx;a2{07ucPi#%Yog&_(@eS2$^1x~OYB zUDVVNOBKYV5+%sp+zx~MU8~~=v^}#+ihhbNj7^fENBfSr&rt86ryQR1o z-RI_@ajS=9smubpx1O=*i;2Z#u9Kzs`D3JkidyRC;2`a!e<0fXxwBFnA58AFzsJe| zF{;=y{(eey>5F=v*>#pZ+E8bxZwojQ<%B(icRI9P1JWoQSBn#I_@+)5`(yTtWcaXw1Bb8Pv}M(oJH&+mA-aj~L6#b=Rn0(h z>TJBEy4A@#7LgOg?>U@mJZhue$SfVKw%R2QG^70aHWy-IyW0p|S&f*)pJ%C3 zb3~%E*4@JiWEN%>GP|oBQ?f&3imF{=`+Qls$j-^J(cVJe+ByF@hWGiRC-fSMe7`Z> z`SukDVBNq)m?M!D*~I zz6|}H7BP>7xtb%&9yEFCaVgi~R>wyJ$Y?*tmqxnxM5j4}X>Mg&}zvDBS})rJiFb<0?w#~gqX#ws)< zy`M^Z5iF^G#4MFddGkLAIHCMKIVe^+J1ql!pgQ+_Q`NO8>pSs6*e(T^UFvsB!~}J* z%-~@A5uaweUtI9GIPpc1vn-JK(CaxA8}Ih3?lmg6R-M%tpiDUJ*AoFNiS-YEhI7$GP({r=BtAx3 z?mS`z%e9_VH6xc8Z&X2B(GSg<(C*S%UQZ9y<2^O8#A=-E9D|w54TB|ri0JL?!Was zb{SlkD@Tiw5%(P#2toJcs9(T@SritoA9vXKOpG#qc{s+~9CNe$s#X0=)uIa*(k&LB zMcpO1Nd8nV;LuY2#;3v`Ll@5|hj~_ZlR7oxX5g~=_c9(ASUK4z3jOjMwL~88-t69j z+R;Jx-RUu9E$h!|r#OsVr+uM0epxJr!hiihY=T)f&FvW*fkMR1qGvQ?fq>cG6cqQ0 z82i%8VE9rW3>21~!>793Tq-g|be=dV0`)iu#h+GqY8NuJ7p7LuivgD0D^HeT^#^gk zJCtKeto~+zS@sz=($ieaD75@-p+P7Z9$*jjf2%S()X(4V-b0mqf9=0r^KnIYwM@mg za43+_Ys1u4N7Qxe)w$g#nj(D=Y+M}8MR>AZtwlOe3NLXZ+j2hW(&e&A4pvtSTg?)4 z;%YK>DMHxp$kLD^I3amlSF(#`;f!uJcvF1&s+EXB#97hJrw23mkH_qibAtqcK^lwC z(a|s)j1s6Y9fT-c+)F2;kxryeRxWYpc^Q{G6=!=jDA8haIH3(;SZl2Duj=(8DZ3Jg`bR&LjwC;~%)eR=C zM5lp{I-Gwj05|5)(qNgr*$P;=Z)A)#s;zMgR$C*c0p{S)yb!rn!3dEbVI64W^Boqy zDP$u5=4f1;a`Zz8^)tgBL$BS21$q z`5P{NC*GHj%33v5yzbs0!oip*wr7PP@CN$?(5Qlh4r_f#=+W$}aqhE{B;wBO5rQ(a@n%A_j2L0ZC>iY;a@C6>gJMHT)$JlUG z*(eq0tLh`Pwm=tmf`|gv-n2^e6eoqnQguI6*7y7-!o6QugHTF0ePeA1VZa=5sd!eL zbT$3o{9*h*JmlV>rmEN7TK7@$JN3RC9#h3y!_9{f4I6l;0ke~4%p+g_G9nPI;w zCdWvLb|0uW53djvD=lqJhC6u(o%M4-B1E-SsZ!oU2iFqWOD7N1aXM9&5OtoH4-WF$ zvvtS@Qnu#Uf~D_$-hFDp;$C!;vp8CKiDrxFrHYU!Ul!Dt<=)~%sIRvu={$wra-I7NQ?(O4NDrVz##s=_e0(%MGCneTRh|78vqxLP010SO5*hoTaca zIpH8D4cR``A{KD$%HrU$Ai=r6*^lJ7RV{}roW+!0ZU3~phWYEY%n>V>ft_EH3~88GtpB~Ta`b(w|Gi>zMP;#< zbBBko)71g!0l%a}HKOB%R2`+F6WE@PvjjjD%a}JI^vd*CXT8=sOw{B|seN#Ar2m9X zZ%vB3^DLv;5L1fOX-tc{NdH0iG>(sNP&K0JPoIzcRE_EWh6=fFTFxoj)3PkkeCjq+ z>_`?Gs$#Q9M}h?VkDgDr{QE+$oU3BUj+(MJo6U)}hfCLJ6JAne}@0R#<&1 zeyu8H-=f%#$%g2C+uG5^;o+07IbGBn;;$ZhZkpI7W@#ILJ7H|@^ozTZUfc(Jdc4i7%nG?@sK@Zd zJSINW@211inkuh8Jtq3!vaK8xNKdbB_E;K z1t@MY`0EnwCfeXFw5Ko*`V(>}z}RnRp9(PE+hb$>YB?;|7WSqEm@Qzk#4=ngPN6uW z^}BO9U^OO-buXroT(fuAlUw#ffmRq3Yln{?9=W2=L!vD1l*u-hrUE8y0hZYGY$32( z&k!BaswWKrxlzv)9aHE{6dh{Q^MruHqBZLjnR4_r(MhP)vq}}fd;f8g=nQ*)oFh8h zRy{>@`~p2ibpDlkhUh?no*)EX)YC&}*~jb?$WVN1FRxG!sdsMR*l^r8LHYWxU@q{7LlwQRL-qvS{W>H*^ZMUQzFdC*9r{3j z059rY{RO;GhW-RPWWV;~H}LLf{`e8RocHOk;HCTm+{vlIZ@vBEClPAnvQ#Tl=^VW^ zar=`D?pALj`AN(GZ$J4-gqrQ`D_%^hL5scp<3(><@uIzUsb()4UaqEZe_8+=p4Rl$ zZ*GFApHPYK|bSgv#L*`hhG-#(S4Gy+VY(zo67 zM}Jqc%AM$-o5W$X)573osrtqk)qiZCE5qtG1`pIADVVm+#NH85hoL+VO8vRH!~>KaO~*~|K&|N zK~7@)w4w|&T3rXR(IYm|T!f}hKJu0APl}~aijh-c6k35qj%V+3F%7O4hvH)2EO7y9 zm@h=c%VGd)_dXc;7Xor$i0q}0$d9^1#ZFAR4Y4p7=Lb*L=w>EHUZ+=846J3iLoHv+ zS!jYJiUG(jH^ob+MqLw?)poT%qwOtsu%2Fn5v?(X8|{4fOhz7N-iEoIx40KqLLOzR z?CvVdgbxi= zW{9<6&eXpLaVq{4V^um|1gOQtce+wC-alA+d{?wSM}U?KVw?7*P5zf?Bs{DdpWKMX!yom#B&Qjdw>*(JL4^=2YqFV!VeYXTD% za*df$^0pQ#CtJMo=`+nGJjjl(q5`^)T{Ywft+(3L@g+*3Q2>YUkPYi9C(Q z$-^Wn&pnMG9tdrZvsBvnz7IcH8^idCF?rnz&nr8mREgLK?yVtJAm%ryq)|8eSOlL# z51D%QV-r3p{38xySww~8tkfReqD51=wB-`J!=0zc4S@S1^xd|C>{}a*%SOgtqn}M| zF`HPuOP{N)66jVVx}%IjKN~pI!NY7py74zKU zH!fIm(NhDut1-(Mfj*TM8Q=G@C&$O-FQS~QYDCi`@rOlGg*dsFI8AMq4LPuFTkq3$ zvjh68%7#R%mS9Q?1cl~X-)X9&a%C!$k+DWJVX(8E_;I@Hnu2nY3xixX$_8HhL~%+m zqg`AV6~CZ&pgK-;@|~TUahhSO9*a&tDu#u--W7S-5H&=s8XHp=F~%uD_(d|2^kS4PZ1uj5`YzKoGTqNfRw-#@JU|-2$4u3ELu%7>0^Y&t)>g z-c*Cow;2=Cs1c&!%XC7_s=61Wop!w!pWqHlIZg*@u(_bR=*PtX7e``4D3z6whB=^o zrwK54h)+UUYAqXLotS6Y0dc?HgHisfOSZlk)w#?yDcWSqAL1{{m4<}NK_g7@NKmf*VQx&QS_KBGQZ5Z?>-9rKcV(*cRBzQC z!fFWZgFX8)Gqi@0T%#ZU=^GS#Zy*oQkOPT&eKj#TmRRoxMYX1cv!$K=a15*}D>&<&~Q)O`2+TjFlibhe`Z4{lfW(eh~Q3z%t% zbW`mHuXd4IqDKEiohhyy9CuI9Y8+q{r0p{#%0YELx7<|yPu~Zbdxr3RAqK~JHBDxx z7TN29c9B+OZWomRsvcvtJ{|4jn|>W%2bx4|{xctFw)ikCv(z6&yW1-Uxm^PCJ;VO` zhuKE~YXC_ha4+ElRKbtnz;i`>8`u(=vxS)=#mKNMU>3EwB{8+e&Ph$yS{rr1kh@m2 zMbWh!2})=l5zA8O^Mu)x&U zVYGEL52T$oG^;}~RpwXsex+{0V!-LK-`zawftw;6;40w=?TI7BfuIc_&dAY3J2YyQ zTI>em%I9}Sq9!P1fryBV#qS)6RdGZwDzluu2co5Yg4^1Is4eZY8dH02AR|8&5alBd_yw;Ss;l_)H@wmh0TCdYDe8m z+xhCR$$k;|Sd)O}Hf)QF3!@9fInf$XfoUvzy=uj@oEQ%udI5`5i`cB~UiE*@u!sA_ zmS5p$9B~liD`ay z%X4hmX?Kg80zd$sS6kS$>SXke09D-;vak0)Hq8*@hKR}mYGjy!b}=OT19fm$oCzUS z@JCg%TZ{-J^2!AbS*JNq{cWhmVsWo2-u?r~@#~k0M>q+XH?vLpex=U53pVooe$jvY zVdY<>GC3@mw}@w9q6}8cUkHeMX)cR#+6X562bxB`K-|T0d;&D;I3)6af;UCuVJ;yfvhP9 zRT5A&wUEeH7-IbnWNGW&%m|{2&1#HWJ|{lgyA9QJ0cs!cCpe% zzo5l*pI0+EP`p1qX19q~@D#wG3~`s`n`CbfAt)TfO1%dKZLSCk6C;z8IHMvIWy$ca zZZTb5ELqasD!roA`Rq11#t(~Jb4%;_>Fe@126;IDT1G)AiLaXdyC9k_6VIX6Ao2%t z49tojLoYs+mjZi}fwsn{1y z_qP3ltg?56<6YGjK2~nLCuQ~IbL9_EWf7KBUDy8paVfF&Rc$2hFsCh={_@M=ms?(* zu_$K$*;^Qmb`U!uwZy#V;4PnDvt4}B%qrGkXvoVT#X)_6DSAy8!=qu+?tSYaHUEM@130;Ds}`BVh26c_i2+SPOCqLHckTwyjfwN5jde=Y~10Q z2B_9&gpG`gKU;p5_$IsxfZpoM;$5JQMiQl31jay{#m?znlby6^k{oP`Z}`x?#DveW zjtBeZCbMO3%iOu)ISw~*~61p8Ul6Adz~7?o}pm~R*;!q9#R*Z;u;)DU`I_2!byZX*HoA5d!R;_ zcij(4rHgC0XPK=N_nF%;LZ{wkJwDzPmnMMXv8i7MBu~E0wd?*;eSbW!{;s(V({(&$ zIsGtk=iRLIiV$~a4_E#g zwu_o7f)F-?%D!+spbf=#R*rGt1v=s7)w`Kb#$civ01nH6Q$C`~4Hk%Uakn|M@53s6 zw5sSmzB?*A?2za4c|e8y-7L^;o{Aq-F&lA;Zs#&0W zWJnwPlk?2|y41dMWXS!6DXx$j7n`xzGAr$xkcPRH z@i9jEFDt#RXRol@%m!Hk<2MtUs91Ey0K_2UDeMblr$J>M6oucuvg?bzG4TMWQ&ilA zucs>L^XvXs^^Yfit&L5r;ZyzAeB-arf4(~`*eyh{8*r;w6=9LZ#5_}!AL01l44361 zD^P41Ss_(pV_GNCm`F3-Hk-|)DS@szeNaP<-ZfFk7%a4lOj*OZP&CD8Q@T7qaYjzxwP?>H+bJoD`P#LB1e%ejWLf7NyEB4%*j~F6{Nzq_5zqD}rLa`y*)b!BQ2=-TvwOcQJ29oZRHEDTCQ8F z`>F0mwU2)KN7(&`ARz0|DQTdeCi~@yFNi;j2e-*MOi;5D?Tt||t{)f={}WBF2gak& zcgEZ5NjRhCKpDr@$b;^QFX%p-lIpWvD^TB9A}5FCWI;ge@?05N<^iWO^y2UVFZPdM z0ILL|EPq6Nvc~5p`oy#GIJR!as@qrH5Xi89WtoM5ZsB*C>I~O! zd_6$XRgV6Mv+s^#;EL_66pnDYIOFi8>caUkcc!II))ve~yM2M-J|dc& zDlOVfXJ4;DZd*D48z#wSlEB-9LqaN;K;neL0{#ARd1wmK=exRR;asp)3>+xc$iC zhqk%f11S9mqroP;)x;qYi&~&LXwyn<&3O+Hi}SGmJe_fHrs{xRz^hz|lOK{>{31VZ zzq(ohe9=Q3;^hryQ+iCSX59SVKl9dGXN-=F88fZ<*0}gRQGa5Af+(zjt_1bC4spd( zAVSWIHQfb6P7_`lfH}s5A5kyXa2lNZ6*8gIj>O&fP;Tkrf8^ztXD*2>d8PS*#qmU5 z$}bXqnR9&TZUzlAHaa*eMR7 zgZou?#CY+lWrAe>l{B zO>~~)6J=3%po4aLT1#*YB>x3b{$`6+!O!+>-MRiR-C@oQR`;8qieJ8TRAelPs*|qo zahm#N%w?2_+sym!y|3ZkhD322Ra2=(TL)f^xezlPMig|mM|TsaABH-;Ua7SC~{>O}3p$*i3i zM0mC!dttQMmbL4mZ9s7Y)}$aW@Kw;A>Y3<#F4~1S@*2?=g-KRyTaS8I%V?Pp+~ef& zw&=!af7T0xTHSrz6w%8JtaIB z-Gkv|5KenUsVr{eZl11=i&xu5dnr(c3USK^u=u;j;Q?OEiejoI;%j5)H=m1s;hf@_EviZ8$7EP`3!MT;sz(J#E_fnEEGd=i-eQFxEUfsBb;eS$In+~RoChbw46}%& zQA?r7|5l~E|1Icz*9ix=bxKGyJ!w8H zS~k&!GI2aQdPk2y*WaOOq)EJXCkM>OIVv1zcr*I6IRBtl-otJg%WswHQwWQ$TUngo z396e4G`lASEa#AWxL6lm6_vl$bb+d6e!0nSM(2oP@8vGS z*_R2+=E_0f^@@})KA~Y^!#(#rCHv&Cc?gb&IT!N=_F<<|+v9&gMekF`-EcLuKw5X+ z1CR$ToElc|@e)(LmuS^5F(>sBlU1?z5*IPQOjs&cZV^9Yz4yiEJw5m7wzhlSJ~`~8 z+ThZGHNnIvvDWSsy$Hbe>=eI=60^rG5`QP)6w(0K=fLQ~KFB`nXc|(`s#FB5j!|}* z6&NWV)6eQq$la%^q%T>AUTJ05wMy>OIh{g^>`jpe$@hH_j|WD*yA)gOV)ll&L}q-b z7;KCms2bp*=o4X@%(6@DLR5nC?0}sq*1DDas)s|3k5uVbe9Ox?=(Z`}6o2hrRmeGW zVnD4O3;wc2xnVbxqZT^L29rDAAh3G zDe+szPM@kv77NYG8*h(MSnTwk>xL=-&$&b3_|{bgk6qPnbeo(yv5tMhpUX_ii8}9z zXM)}z0B(zA`iF4!y@0qBQyQ3UcFvKghV@1O>kGl?s7&zR$SAdUGS~)V*lQfu3avt0mbyTr zywZlzA>aNagv%fITWLmRtIb+em#FkQrZCSzfENA*4BQrju?Eztlmt` z;&4WycUM80b3noRUbGJ5N$-}LWwE}`NWxRHjNC`D33bp5Jrc~oRU%PHZz>b@B*-g& za!+#d2#R+`Zb*#v9(pdy1J6b6@@yTEd`ln%PW0ffY)rJv6Qap0Q*4X*0**| zwwoy($$#|fw2j0=My+4!3mI%LXEM`u$j^qg)UKHp6y31ZY}d3$b%awUE=Zi`>_}Jh@KZEr`pzf%{?u>HlL~B1=|=E<=(=wPZ5cG|H<>^as5m(hiUbfu_w`3p~JrORK53Xp!I zT%JAFIOY-h>IGPz_I_;M1#LkI4i(kj2G{#RP7p@J^d}GhhqqvEcD;sd>v|0gv;EwI z3l=*ELYPfKr^!_k9&v^{8n|<|#eTpc8cyy~2NhYk8ZBxtqFrv~P3zp1n3wmiRM1!W-~{Mn;oC3*0dT#e`GRQa7uua# zrktU6%T6cj$*8`{N(|nfAu{k5fVCx5E@r~%1N=495SQZBPP}GpUbgP9-)Scb){e7d zE}o^&bLqDh(aZnpmOIYwbJ>Uy@eKRpccX5Hy*UKC_*wNVi|JA*h-&fdt1;}tt7xqX zoT++6pl8mbifVqTYcCN!Q$=5hiw8{c?9E_(tJP8sZ>9{r1nXjPrM5Z5Y4lkN?tn*l zKZQJdGt4wo#Qq>+Aa*0VIL?68x_Ix}O9j3b7RF?$``K5;?J&lcMrageaDGKWMZfO@ zsrS84^fSdNqTBb#?edlHI~9(r1NtR1LgEQt@}1RJ54`C5Utbl^sIz=C#gL%e=}r^# zf*IG?FL*0SmYimaJ6&XAe~g;$>4|$n;^Keof|~1AvO}HL9zXx73ufQ8VES9#r@y=6 zrSDq;M_ZPT8xR%&Q=Hywr|OTgM8E0z7tBg5ia|7Bc^qQ_Fj3eZ8OH4ZtJ}TxnHe%9ZqmePmY4s|Gky*wL43!lZ zS$=3O!m3PjOWi-x=3%H8x!pso^0|JDu5!|dD|Ax={RgPDSjO|%=NS@oK6D=ri7UVT zLTHVhEVJ;At4E#@Q9UmHH^_zyKH4MVo$n1lV_sZjo8tVt)z7|CLnCOmoPbf67WI(G zRws#Z@eFqansL5KA~WdL+HX2!IVfG>d8gB=&nLh zw1Y!UQB0-z6^uG9NH6M7LyBYUzae2cNP)*V+r z6s_!&rvh_=9R5cpw~H|*UO#=HGqpCME3vG_yDo5j7;tS;?UGLvaMr>$$*8g$Y!(K_ zM>)i7JG8YA_SpirJnl+Q3Z(Vf$fv+$cdbf^f`-w?p^m^w3L246z|2KEk%NlQ(lDf|1VKsduOY-I8+TA`s{mc%0^H~h*uYWN2A*&Ew5WZBRq!$%JrF?7g~K%d?!ZIH^*ykIXAcxBijMneep zRfAgYX2r*%fC4{5v#8{(*e<_~z(H2Ivk_bPMvUkYn~P%V3OYIcMGikK`1PvM&05C5 z=%EU?sKUsLD1(Z5N079EdPTkqd(yMx?0<*a-WX_8*-~ zjgS3_6;6fWe5+Z5n<6SM86E{e(UHqh7Boe~UV)yLx9D;M#}OTM^I- zb)icSj1vA9glpn$Fu4zjjne}t#u=k7#aa?3!h9?Oa!s-oZac^FC>SiN`2qNy0PTY=G($HrbUXJCX0ZS}z5(OHb$Vm;5qIQuZB<1*ZBa=ndT zt|-Jgi_#btrEh282~edW&mSI}DrfVpu?)dV)U}Fbc<_q1CC4ycES9GY@DaVJqg zH_j25uJQ7h>69bE)M}>-K1jhou5PWmOjqbv5|>BaT_VTM68#A{tpxf-lcT(n8?bln zw(ZtvCa$;6T-M2(8fy<)qe0f;QJePYd7^#`y@TjX^)Q(Z| z<_q6Jkr&2gLODysY?!&#bjg?p5$!Vw9u~fBdik*S59p!SBG%b>PgR6z`f?HIuVa+2 zKl=(4&^D3wU>r<}8uKvwQNCCZ$#5S#f*Bxz<)JM=;c3BQ4)A5Ha*Z6Xi`h7QJx5$I zEI!p-A*6u?xUxj6IWUnaMvgYtt=_$4Z-fp@?@8f#%E#h?--z8_C1bow0OyL?RI&_h z{HDJL6D)GP?)0F$NFH*U{u(5q!wT%#6HGpdofibSSY>_^ze$9Ob=*yMy?B(sGOja% z?n5}zJS^@a;Hs|16{+PnAGqmPipwv zMF?E~4@Q5YscZC$wjT%ooZ!C>d@Af3_(0}Tj6)dsR2(_sqs#?q{cf*sqv1^N@~0l zlN#>?QsZ5pHU7UC?`&eJ;jSC-|7y6qzSMB%ZBxVD^`(Zpla(6nO!U-XCy*NJiLBIE z=b9SpYIfIH7qk9psH?2cqk0V-|GQxFG8xPkHO_YS10-=^NgxX6iA#;gS+;rrf_)!$ z!7t^?$@c^k{{5orFVRF_xp?xu!9)+acJgD<#8gq3E%uS=WVvSYT{^nmIdFF{IovJF z#+0(I#`UG@QHQX7PMPXZdr+6DT(3}tJgx&uu9}4d4O&%Tk3nO>otGHLdhzg^L0zG8 zQ#C3#Ri!%gr=YG;xv47cS$7Ais!rRJE7NFgP@G#q372V@Hi$*e%B}z}g=eUstHj_T z;*)4{69nEEfiLMzfZE20TCY!>hE!vn)>LDi)>LDiCaUeuiNPK`jhqJ7}g_EDb2XOwJfygCi zCFuwIcN&ONB?bhOTa_DH0($;Y%enMTd=cu_dkOn61_ddOq)6jvL4A7nyh7!~`l$T9rYJaa#aX@28VKw;r{|Xa zarRHn3>6#*TVr#RWdpTsYH2jL6L(c z+ar3T4)pA;av+6z%gpR0m=T|5`P<&(!5Z%sEsi`7BjUl=cG3MoryFv*$=m3FnmOK^ zzF^rm5{Ss#ky28&Z79GDAX&(&+>19UZEcI>!H z8+_Jfv4fYL^1g~kh~P<8A5XR{z)3d6heYN!4mG(VZ_Dc7AYK_-WZ++d34m%OZ6PU7 z&W&GfiWPit^eWLKlDdl@yPF9w=EtV=qbW00k5jHHxIFgN z<)>^^01-|-<*Hv@9{=)+Q#Wdv;Y_58|I{v#75&TV;AJ%ERCVe_e+k+F4oLqJXJKYm zM}DB2=lh3KrO&L@81>Uss=jIxsmcVs-D=32%~sAp1A}(@7g!YTYgdHC`B<^->xK+^ z_ERfZAq)Q7+m$7YldXER7bF+3)8iP)>Q*;VZ!VRb>NX=&<#4 zV<0(aZfM-G=PZ9of)BBnmv~0_8K=ocM(b8f9J(2gx9A4X4)y52;oyFxQ-yQm+a43? z^B2v>k(M9QC+%(QPU(m0^h0(oF9&E01^=Mhk5oY$gHH^R%Pn&@X7cviO~l{?qgI3Z z(Yr1s#jl-la1G$s6DGz!Y&~EgaJS4Qfc{i1rW^zKgm-J5J&>2DX!eE%Lg)P$fW^Jf zvnQYU7G)`O@z9_b2(-+mK#4RC^Yie7b1nZY)Q@Y$;>SXxP+}SoowExM54Bh^I~4M~ z&BjNgw4_PS^u3JR-3C$s6rkIhJm9^a>so<8sK?OWoR4yc?mrUKl;}9Pk0?Kbf58el22eOKWB`^L98Fa7 z4ren3yq-vAS!jKTViN_qbYqUH_rrl`VApAY44=%ok*3g3iIu4y*lC&Cd}5B}hsMIG zcGU=#+hGp`~H`2i?^hVWO4QgRbhyNDVxUk4);E5v`Xh-0O)Q36EYpf zR8fx&J?6ycZZk66ecE8Z>*7o$v0Yp9Ph796kv1#SRM|s0up{v*RB5o@L|_X8Ge69$ z7P_-i=13De-90q8OjWPPc9%N~{p&#){Ik@#`l3bvojDioZie$-Mq*h;axN*|GWH-` z@UmFWxf$g6l?Se2&zWmF`4%8nT<>5e3M*M16Y-%3(_b4J(2e9a4dIh`% zFi!+Nz?BX6#OQ0Bss0jH`Lm}-lj%tdrr)6}5l)=H7a#Yh1yMXIzc{*7ODgd0t3h_9 z!&GU6hjTXU?-@sJY5698j<9{;%Z#-Nq`x);&?HX^=w;>MuP zCkj&;#ptMWnPq&HJxkwP!85a$MtKUEL2#j&$VjBK5@x?{<$RSb=6B^3x3chiSp{}I zFOy$^q69-b4N!`&8D;}JP9Z!l%QxL{vdn3)z?ek!VisU0hE)_PXLc9b;uI#NH=wWBEQp4Z||WnRZ4ZgGHcM zGRw(IW+$~z!u1$-qqAON82`=dj8a#I9MV}ddeK=Kh83vs|I>q6JlKl|Ez#yrq}v&w zJ9^O;V*_pYmH1U6!~VHpndh7GIE+5MDUX-O;pXuT_qh0Js_r{&6@0iFjuz51ph?43 zGn>Q)8fO0-4++0YQnpu=Q*6J03I2ZG_d<}=OE*b3TML+UYYfYN*Mz-zy%-LVlQQ`| z82?Vxoo2e!r>M_89?aUC@;Dsn(Fae6tdKX7Qo(;2O+Q5bWJIOnQlqLX+%>W&>e$$_ z{EZ>q0kkRI_~|IiK!d12xBA#<MiBW}LcSST2MzBJ3{I_j;)5bbZfg{9JqvCHuL+M!o&9 zX!HI)!9dXQ76xB1er_hswX$>KX!l1k2wCzJKW#Ok0YMUi$5}eLwWs| zc0hG|rY6cQ8$0eadMMh6jp7A}l1Be|4Gs81@gf}-@A70pD#~&!&N0w8{yk4Wy(h*4 z=ANe;&Wo$%UJ=#No}31NwPDVq0QC#Y-4i;m;c2kA%PE9Rsd6pfJK_yZ&%R?BunK|d zV9azMRx0F`}*NYt5~_)RF`+6k1`&zX*75kn>25f7Xz9(@R))_LM}^>U7Q9cB0aP}sy3 zV^&O@qk2zAxYPO^H%3>DzMtN9vvZRl+ zeDm~QHPYZ7ZHSVPJgk)eCl9;01|OW9?LEYP0Klx;Ek*EaH=#e4;e22}eFH*fF^u2T zmN+&hP6w8)rcMVkJ4`f1#MD$0;v}*tvZ#CH>Ea$!=q&tjATu?U1pUJ^WQnPJT(bDd z;-xf2z%Y7Ah@5n>UPUeynN;MZG=+h(c}bi$ed^@A@6pZ7`kueKO*-Sr)ARCFR>bll zxFY<$XZuKJAX}a~Sy$?J+Obv4(NaIHpVMtPc{<5iVTvRdnJ6IC6;Bxvak74zM|2r+ zNVD%l@^pE+dpe5n)1VtVL?iGMLQ;nwM(-MmmC0<+!`Q$y=T5sZgj>N~2Uo|T6~#!q z0{Va~)U-r2T9P>9VZvVi4qo(Qdb+;c_y_XjU>L_Y$j)Nz;*T~R)!MG?UgqFAuk58o|`TvuR`v1j1&H2 zRX5+g-n|Y(5Z9q`oiDF*tGKJe$49;-PLjr)Hy;9i*KZ|FMkANRvf;kTD3 zRwnpwB-f%Fee7AG%l}{0rPmZ~A=K*Dpc{V_9LWz`kbk}2g50GP`M*=tv1YTSUM4u| zR8E~kh9wU&i>9>?W|64O|DLIO?X$EtFxN*%a<%kG~3gi zeohH{NwZU;uZiYlNwQy}IWaw10v4e&u{~s+fZ39I&7gx9#f@f0aii*ER%fBfN|erz zl+HpwoGflcee70@x8E>!(WKNMsW3kqRS+#!TaXa^_1tI-cAkx}G%c!cZwRh%Ux;xS8H{BfqB{QPh>~6;j4` zrHD)`*jDKETCV0B2HPO;${&Pmf`Cf3CyyGYq#nhoa-nDsdhy9dGK(`)H}+b6b4}_7 zddh5@bF3X-P}_rD6?iF|x|(qw2_GG6;B8i*?c>zr*rV&VR%}b}E2;Ch#;In9bx>w_~WSLR4KBnBrG= z-Gq6Hx)~_Bsn4h3Lusl!+XLmkOnK}zGvZ=0dLL+P&WVU8Q2;q#O`gF9GL5J5)JqYE zoxWb3!-0DYS!<@TSynttb<8w%4*bzNmSWur_gcY~lTvTv_17tduWlb0 zw>b)&XndefS%P9oQ~36vMuyA@T`uBdsC;U8LiEVL-8^tx*Jwt17;Jm4tH%agJ{S{5 zSGO*AwzqX9Au&yhM4j>vtQRa4|fd>+2G6rbLqwW6H?LZ2&Jdwv#HX= z@wDl0U2Vz%yUx+l8!=(RoDFq28AczyqpSACEVAQu_*70<)8(Pm+}1bOaN|wxQ=^Fs zE&tB{YI2j`et`Vm^wtkI$wOW9Ti>;Fmzv#~osb*5%IDfj5Iy_v2bny&A>sjlzNq($Cb*EgT(ByY0oGM?Zr zHQRMsKY7TT?u6~5a%rV0HvRW!70){F(pYa=aU(8t4q?&A z8(~NP=FIYDm5#p?$-OtNQaAp6-n`O}-^b(L#Nx&try217m(_+umV5Z(uD5m==-zzg z!{SQlU2neXCS8tJj5lE!CEpYFCag}F$sv4Y*BjXnIF;U9d+1|!I}+*INlE996xR4 z?sm7{5B#bbEnx`+yBUxh#*XUI;dSdxK#D%txLPdjgJHdt1ZcqXIn8iZ4kZ?v^4dR& z7f@FHGV+7Mv^Hq`1Mw1Z@5PtH5%(|EXlldSOIgNnip5({dN1RC>Uc~cY=~-Oz>CCV z^0#B-_RD~AJx31BVwEQr}#6Jb3J?;BGSN`OB0SC5C?d~%fl{m+6YQYH&xYAM1F zju%Df3^u82jv$_FMrAl1L!20=(faEVSmI{>f5%IY{x%pT`+4mAXqO>Q)%8*BC&c^knLt$&DB^%CZ7@fVP z(`c*xTVffA+cJUUh+=$3aVXE4yXe6Or4>~r)RO<3_@Pr(pLbR5e_eWGNFJ^dl^wj~m=iKO0d==$| zoVfi^g*(}p(sOFh{@RsGz8WdkE?P9>ktH$o+KlGOm|va*hH{G7fWO8|v1RgXO#5><6|VXV z?m$-yT)%K1jkyJW4!h5&&*d*Gk*bKL7_F=IAj&8a$s#`^HJwpijf@uKJs8wy9M@^N z@lqs3+05}t5Phesrua}jvj?Ls*$C&T5*LzX13(O5jimn)mAeg`-2Qc&L~d-0DKp_P zj05F93)FEx)%dr#xMdH7Y=1E}jFe}AsKFq7f4N;O=kRdDMNz9;Y4(pt3Dx_es68e* z50GOmy(Y$m5@5`v!ckVC!&+qd@9N9ymDU{0IL9)c_I&&9nP?{NboLn9^|;e9*R*d> zb{GPAsJ+VShI^e53WcS185|eps=Y>xLs$ZMJNHG!04)L*(BYQG$NF9EjG#jnW6@aT zz9?$2bYLd8id#Zc)p5Olr%uqrK8NAHJPr_dDG=dVKz@Gy9f0)>EZ#n#rC4|1vo%od9xp}sqJR&;DgGGCsUEmP zJ1*`*JT}pEM^&ni-H3ZI4DghI990>^n@58f@0X+AMhzk&4~FGIMZg{PcFeNp`*?1G zskZzXLnbfF+OVu;8oeyG{5huXfN)|v zLO+es2mq%n%Yfs(_mewu+Jt+dDB~SP1F+chVm!}>3~0T8m@~_^LMRLrh<9+(9v?0$ z=KH_huxvL%WKMyYbc<|2&n}UN7<>Qf|H0b(z(rN2{r{-*&w+@FL7cxRD3lZw6eN_C zBoq`B6qb|}6qFQ{>}so<6qJ;fly17|u9lRPmKGG278Dc|v{<_7E^fGsyI9)luD17? zxzEh=d*6dr`#jJ0_5J?7&-Yd8%$ak}oH^&dulu?_*XR0NB0{seD)S1c-}XM~Q*>AS z<(U8GhN=hflSh;?ASV6YEhilj7hFee2#=`g^dq7K`QIOrS*{d4lpZtevW0X78>#IO zP0C;~Aw;XTPa9_zC-r^ErkV7!v;d9!zC1;TSSmg@NDQ+c;zo5g*QV794dIay?hHxX z3_yrNh}yo8D3*G3!!ZLzu{7o_{K7+T7CNz}fJN!JRi4D0uZ7qkr3wh0W{LO)8)BAWw;(A>}n^s4DI z*`1S?l1Bp)I=o2#eytyY*k9~>Qv1_;a%zj5 z`HZ}Nav)J}f%aYO-KE`Q)Iaz+L>gT$bI3JjZy3L#-rMLAac0o<6WSa8Wc?80gNb@- z0X&L#eGed((jxw%U7~$JH+Hl3ofKNA{vj*=&x)^v)+ zdMb$oBt4P77GKG~Xti!_5G4;ix)xT%8hOltfLf_2*Ib^C)6G}_u#7wKyAaTbP*v8i zy+lsKg-WBioc7A{!LQtU1)Zm5>o<(gLh+8BMl{+@Xka{tW3h(b?{4|YdqKNCYrfnp zp4KmS=neAsGzj|4L+5b)0vc|-tX)H0t_J7V`1J#=6-D+dZ>~C@rh9u{u=>%*2+YNm z3$EziXq-DA1#)}>t|^)jIbBL>>WA&(WjcE?i2c*ABIQ36?GS?HxrF{?Nj2NczCgwnl=)-I+ zmK5|gsa0(cMTp7jY2sO_er7!=YXNBAOQ+Q%fF32{S-ZxC;mwu0DMgQw?Swrv68Kh2 zGf~sG*ygRHYY@VHVXS<0Ji{0)#Wq?c|5>msNw_<$Q&h{56KcB2K9hDJaIaBcML_)+ z>N-9O7y1wq_5YFvj_+!zQChP)SM;!0=N8wRep_wy|AV^E{pQzui~{Yl4I(m4c6nHX z+u4)nwMM{ua4$7!@B5M|{d(q6)8oHV`e(?bCL0k|%S4(ZQ!nNSv+rr2X49v7bB*2) zO!^eRN>iDpX=}Lo^pJ9DzcI~$BoigCv~&6$|4%Q(DW4-d@$0C6|8|0g4N+nz&D6LA zD(7g{+Y1hzb~n$9X*4*l#39xf#ObXlv|O#O)CTtcx(z0S*aBM&t=n_oBId3W5VB@SOL?~2A zB{uG@mQ;vn>u8lg61`m}+u+_sVpFojgss(mBGnSTUE-LJN5)zVnjjH=l_<;DxC1-onM9wx!)jI}R zTuU9jy{k8OdghBNdA6cnuzLNf^^Wdc3uL+{a6&9KZ^m1d_HTsiDsiJjqw^UZ z`Ksu05hVv=F)*z%HSX`ibDi6J5z<%#-yK(ak1Z#$3@u|CNZ1yLlSE~W_!N&(eJ5t7 zBtfK0M~?-siW`+FvqoU*iJ3+kdf}6yDI_yTN9C2&hsxXhM}kM!bMzzPL6`4eQ9PqZ z%;d7ulSUbu5FgHbrj|q1JlfX3t8-VsLqvC9fYiC&v!Qy!{VzQ5#A8o9`ov?FE94Z) zFc-;OJHt1Ia!J(@1wIxlNv=FwESf2%xtFhgV)gRX7PNj#dPc-qj^6HhdE_&)RKaRU zl05ggOm)aRrGvP3m7MMo&j1Hcf=+%X5sZwCmmx_W@8A6|*!D(= zBcP{VSRH6hDRmdz8y3z;*`zO(i}ktw2v5A(@5H4&q-C@AuEgS|YD&MOR-SUtleM;B z)DjnbW?^`8$PvflceLVxkkA)p9vGZn8jt*5Oc+ts%!#|wG75^)Wb(`@zxhbUsIpOL zoaTKuSoNlPE>U;m7@ae?p=zE>CImP1vB$4mzPV(XQ7EKIwE=gE940;$K~l9$^dP|i zCvAX*>7Xix9*{BdCpmeHz6J+}9CRXH!5Z#kqO5j*<5J%`z&Y2r0xRKluO$RM?8j}Y z5c{+Zs_s}@s^+i13xL?h>aQ}kwJOOgLm+R)$e5B*fj7tdYGSiwnBC%lK^Qvwhlh&> zV`mf;#b>nTQAep}@CiM{_R6O%*Na9yTFpH@QB7Sn7yWP{Pfy{+N=YK|s9$G26^S!M zpVlA-^af&cV1YfbQzKN@lpy&t;=dz*Ht}P`-L3#mL$iWf|U>K^f$V(I2bY0=2ciXLMP z|3K|vvrdw&?1XEcbf&>+O{Ve-M>UrQu+IN}JEv(OfjVEEk3p0v-sC&9D6z)n@8U1E zW;u=dHU&SK5GRHsi%zyo4k~vzlQ28k_V55x=%>33pV=i@vP&&fpQkN~eLv1Reh+PF zZE_%k*|5gr1zTwz<~#a;_^QtjAGdd`v6FaodCy zS+~|rHHl1K#t~sC`YNV!I+3(IVjiUTCfB|pYGn3E#ti$ca%zxg7i?a;fSH+Xtmz(E zpvvNyhsT+ck!cDZIqrcmi@UuBNj`jiF$hd6r-1|2vOzs0Mt!c+q}zmHG)<(gLJ5$l zoVmbavU}|23e7c2UNzgUmr|C)j9)`XgZXS732x;9)4xLps!lf(q$lEuCQ@#mVA;50 z#i}hH&AAJFs#P4ci$ks&GgR~n0><5PjeBG^7Z|g3zC}Mp=Wl;0W_!i@)d~tYc7T9ftd`@Aq--5vUB1dL6g2i&ajX~ zm%m9LU|b(^TBCn>!w+uvw%Yu)TC4X6n1<%0P`ZUGe2spNN*o4~mO56i(g)~uPQ9mo z<_$_F$ajdpsk(+n7xp?%O-)mnQi<15iP!CNBYoad7I?Pmnx z)ovN)TV$5+KPoQ-#a!c%GoVd?Lrvv!K*6xQ zeo^RwB@gb#5qB}tsM&>jy^iK^lIazev=`hsPDHp^hZB;h>kXRo#wev!aDYmZY%$Q? zyLxS}dx@wuHdL0VW_igoj?%v=*hb?3od3g2q&Q-e{B6T!gW+^Ym z+v7c8WBE#Ca>C21Q21>E4g(4zC*x)H#vpuqgsN`t$zca7ThzVk$d;F>HdwqChunUr zZ?Tx`Z`9@zITrh)HK(^x%ndBYESyoXQ;Pbn;!+n#g{ptpBX%kx2VAHS@uHRI`_SA+ zi+_Hz=bl%M|CHy*dE$Jz!NF>-)H$o+;jLkH>%)1!?vH3*M0r3FRZp~3`eAvDegD`B zt#G^@HGoDXS+|{lK-=w2G}4_wdY#^LZP(0{6X(f`m3Un4UJ*Sq+{+Cu7Bl3y^b?-& zR;MX7O2Ow9KST4%ARl$mjNw)W+jBH@ohIgKXM1qqN=^D2O*gLh>M9|U4ScTc^d}nW z{QdnBxrHOXvCDtLM|@68g3#Oz&G?(m!2wY^_CvjIne~_S3L~W(jb>3clou-_a^N+W zc=QmZ!j06TogUnCtlD(>{9A7AE$typiy42|n6Jess(nZb4|b~D^)?-wx01~Q6jMQ2y|}^*jk;cJ zo-gip(5Dk|D<%IB3=#DG9=UW^N~f^cPYzLHP{vrGwI*^LN`9R>Y?cLG9%iX2W2Q7A zCu0ytW}&TF1br^juI#Qcfo_$76aIEl;SKW^`f;r3T)fA?rX6guu!*K)3Ta1JBh1#Y zwrzQbV`TsCL){+bMHhGAJ7?|fUA=xkk|5@WMe`df!NfQ2Wkm0k&HlCKYwjy5OLdgY zU%L@u9F~N3V#+B&EubCSU<5wyw3Kecl(t7=4@2g$qlTI0Bdn4&LC3o5v zEUTGczGU^Cl9zumLYyx0WPC8b;uSqGQ3XJs%|dJQoigV-io$DJLzF*rkfF; zRymEZ6Zxer{8ClW#ZPSo@F``vMMi%~&;#jO&m?I?juh9VinfU{9sy+Y4|zG>_Q^eN zWGh-#J8_nPRHr2lKiwokHg*otmR6K@;&>M#YJ+6zBF?(houqF9v_EVT4-W{lxJSH? z9H$vK05LRf$p#ElwyXm>W9(=EV>sbzMXtr$-b6)gHNqeJK;aUwPF3^W^hj)ml4dc> z!XhRP18I=;BTK{#r%A)w;6ObbyrjAYz93s!xWjzI8o4`oN$(>wdyjbdIyW-W&Dtj9 zJ{B+JmgWbQde&ZxrMHP&7*6G)8sBQ5>!pnIV!cs~cC8Rc$+my{ z`mNhL8kS?nmzvkl&ReVGB+8l3#U3XCVzYO>`Rms|`|w)_>IFG$^vAHRP~8LKMt-67 z!W+-e;|A;&6*hUuEHg_oWc0$y6)1hkYV)#={S9pjmyN_9>cc9T!@+cHZF^U*C8i9n zaw}Nnobzb5!KXY;PSH2E6yh<}`0|xvAI@X7_wYLuKcC(}D=><$$xriQtPL11geBM_ zGhv2J=N9K4oh6*z+t;qbdyO&E3v-cU-l8_Z>hb1q)w)Idm#DZ@fe>#gNy6ZOa~hj} z;t=?1$3`AGw?)<+qE5AcZ7Va^%sUWSGVC=HKHp>xWq(<1|2#tdJcSq=vX#+pr9q_5Su zKXAR81-@)S5SCz45o`+?RXRrQWHhyKlZ%Pz6J%A;G91D5Hp(MptLpLCbKON|`7d$3 z92BGZZhZ}TBjuo@fMKDD=?zZ90h>|-yi$9?h+tQ^o^zBuSuop2AKf|925vCGUhbDB5VWPFiPn2J(Am${C+ zW;Zm{c`z8OUf8-~gX8_SCAYoqku+Spia;NWiHtjPqgdImm`#bVvjGIN_ zhFPe#WuizN-oD5c?DXbcqdcorTDZ25rDa&+KZfuA`f{2Z5_Yg?BNCP)svKGZCVvAO z@#C?oz$x23Ke$hUgW4K%c!_gjA92E|B`$H~PHTzNeB>Rgq>?mC+@%5#bB`!Sn)X0X&<|$TkuZCAeBfjPz#YMBlZ4U5w&zBy2^_moM_xf3@mTt4I=>WUshPL9sEe1wYm-771fQH^KHNITP+RqYUYW(Sf|_@uR@ zqElgLE)y61L(CjONV#TE!@(`qbWv;$Unioh+ReV5w0dhX>5bksr`x>Y(v1ZxG2X-| z;1DfbtA}#es;#k8mhq^5*){Sjc}#D&EP=f4G#XS-3pK+??i=YDdu93lo<(=+8v$?XUU}2hrqyxd8>Q(En zf?|=YtIjfUDCs89=w}9j%ss#N$a{W|-)j;z>bb{3S2?KV%1v6XAJ~(`PCNE%n?7DX zb`9f26-n3`CKN2KV-V2`=@8Wq5p1oy25a;fvpq{}(`YTLJxhMS4lDrQFq2Gpwn*p~ zkt0OPLn0X?umr>~+&^xONn#H0)X!|ijQY}@ro}Xx zlA$K~)m4N>9eT1|zmQnz9r4vwZW&Jq?l24O1KijyZH`@|m4^6GY9F|rvdF-3=<3b; z<3(J+3cFZwgeYR(kekS%Rj|xGUPM~{DfyC}c`|`&!^ew*=_4a)Iz&kj=3*o+_2>y^ z%xoAXBavL5MO~aCE~tJUG+i6JWL1%0@75B~f+KZo5Rt(SXFCYy3JI4u0|}=J7Kj}p zMp`m>0?PF&+)Tb0PyB0LBBs@z{$)62>R9h{6+pz+7+O$y#&^yt*ae|v6GTv^ng zgy-L%+vz>$#s(i-{MgZE{6(EEHuybRB%_t9ov3(}BuVno5A5n6y2HA6|!( zMw?hhStun2=AAZ~-)|uj}zQ>=jwok_E>QGGVHQ9mgE!UO(`-Y5_9??IAViWqtb!+l6=6o2I3KZ1$_&u1FSqa|#o!|8V1rEjcDOt1( z4+lEE&qYxHWBOL!^AcwM20Kw14JdGHuV#K@0^NK=s~)DfZ6IT8y-kJ#E$iZjmqhBf z;vVIHX{TUwaZoOPZZR%tE;~?hjSU5I(r7rW*HceoBlO>3UL}h&Fs+6p)Xi!{5BMeW z65@8A)SblQF$C>pM7^c>jm}`n?rq3Q!@3i<(&*%NIbSWrLY2GN#QpS{k0OGW>7xvG zvI=elMvQ49Y&J6vwZVLxm*XI$WBZ~WH&^r_w5gS9D2;P!_ zVD?etbqo{P{zd zgJu$QZ}Z346r*9YT-i-yfNpuaFcpixSU?{qinJsMDkQoWgMCHf3A?E5_h-`baGw^J zK@aqD;jC1ujYoX>3d;;*KsI?4yx4*XQt%=eWfRZ0igrbyD$A5!(NrfE(p^#!b2^-_Cm?3D;%W2xFELbS(9t-9t4R^OJVxGr&(TXq%VXe3l59-UX!#I#bQhy z^A@aHJr7K}W>n!3>)V#MIsWnI&&Bv|Pn%MiDE1u!cX-Zp^?f(qamg9nfp{Utsd)H# zSiwD{V16c72eS0HQPzZ)a)#iuTI^!f%p)b7;x_M%zL&k-YLsYvr00j6LKW|9VV5^gxvYFnv3pPPWo=U&)O?(c%P`lYKL6?d%Jdux=DVGS zr-OiVUXT+5eB;dwE?YR`q>f@J1z$&cS_$Qb^Ty$nKBli zv5v*M>RR08g8dA4;@@0ZWMaiWr#aPj27~eIbW9q>F>Cp9aLuo}+_~ZxR&tc!!r}nX zbNi1$F|OS<#R}V|_t*km=Euu#fBbHTw3P26diUxISu`b6Fn;qR`OESjw`er3Iv}rO zS$hM?vs*C0J?@3dmHoBs(BgC5@&QG+ivVkYL922wAetKZkdB)kL4tQB2#`j3a~(}n z#NFoKJ@>m`fBd2a{?qL?xvC$8_jd7&o$tv1LqE>zxnE?+xk1bzM)@+pOi!`QohAu4 zD3UNTw#<aNo zr6)y}o&YL3pml-qgLAQz&^0@rb%jq^OB>b4v|8kCz3fwc@8Fg|b=Tr#{c(}4c1FhZ zH;k${9zC8)ma0c=fz$y&$s3Avv)5SlcBi@hA0(Q=f?jY1NOdbxhzL2^IXL{GE6uGP ze!`aip%ujer($EZlLPBG-YKGCtddXGRyuGn5djnVTRbvx7qZd|q@hGc>xlt8dOh30 zwWTs;Gi%6&9Cl75o$fmQT}w!?eW08utqXPvc9@ktACUJvj~JJBa?swXi|Q4jr8_+g@H-4nvN9MdmQ34N*vf9|s3~8k7yLv`UKG{K}aD_O~eQSbt3ip|OSG zSL+QMdkc4-ckJP7{$USa^Vl|~-syt^5o22d5Qc*To<2Ud{hdRDBALdbCt3coW!4Nv z6lqS?^e@39H&BD$h4qaSYe+|Cs`V9^i7u$}P=h;mxr`_;C~p%7qP>L##f-yx$`^8w zQUIMfsCY62Ij~I(swi`taU&5VXQlX!S*iMdsVDdFb*KI}lR^F2WJ) zPHR!eg(-+B|KoJJ?Mjz78A_k+N|(Nq?CND!Z*tUSpwsaT$~;$47+;!n3i%t8|@ApGayDRQoHl} zEk&6b2J$~%?5~NckA~{V^FQR z5H1Clz)VT{2^uLxFt{%+&6A<-ufaNqFQ5v1VeTj275|n=X*Gb#&kkNCoS+@}B+~_p{rtUY^uy~$ku5raI}dwZHtn9)A-S?G9&gcA}ZJIEisGm&;Ru&k%Cb6_dGQE z@NzMRYDKYW^_Ft-E#@DtYL>L;OX~&hUy4haf~H@}OO3*MfwX@nXQ?Z@OxQjaNr{`mzQ%YJdf&Ld$kIC!PUc-htX22PyJmp}RRI@4HfYu6nMCI< zQo3d0MY|S3gjg!S<-bN*MV&~H+#44q226exOa_^zi01#dcbaLn}%u8BQ zvuf2Mx(1q;ZS7o6B(ih}ziTO}Of*2qcSLe!db1CJ4KGg#m$0NB@lrUKt~KNE5mwI0V|~#h#@JL^gmEpM+nGv>RebHk?=#o}F|yDXW71X{c$Lp9&Z#Yx z$85hD_|0`RzD+20$skk>p{d>PELM3J?=hTxk!;p;MPkl|!56e#Sm+|r>@SoXSPj&r z3gQ9riUO9R<>6lk(@o+SiKbBB4NfDE^)8BBG!ooh@T2sKh14*pckglK!99vqHpZ7N z7u(Vba%Q<@Tn>v$qS7{x=HXX(M1F%2V4%>!G>hiH3X1EwnN8ZZbd*)vgPnhBPC89M z`DKt7@tyUZK?t@mxUE8K)pwqDuxn2GQA*hv^sbKtz3bCkv{oGPBN8W^yNS3blsm5J zkiHa#yB0zspw$tRmB+x&}7n8-tl->RqTsR&wgN1$}7ZQjL-+sJZ8;R28fY&~7M z`2oe&>%&xqxip*hF^PsUe||6kICE|OIHDfg-&9ZBm5;mki%BwiI7y}$-?;In?K5w5 zshcp|J-GkPR~weS;?4w1iB9{Im}30J8*eY`LBG4tY3Z@06$S6#Z1E4*GUIUX;C{DA z*eW9jt-vs49%z!;Ht{;0DGX`ddB;z7kFfz`OJ-+~#;YX{Z_6u~H`^@}az@qt&*c7x zAXP4Mml^weJA)^iF=#z5k||FTUADZUl3AkP)xT@6`kEy5Ng|Q5(+_a8NHiL~4ilbt zW3da*s}H7hk4kyKH>fxINs0U%msq7l+GP)31D#6zi~UVDE!Ipi$`Zv^;C8lyE4Q;3 z+r$#19%~K{*AIe6i~$gX`}v7K0uW&`^<%3};LV+{L_v(~Zucm{Qm$^AF}PXKZ?vU% z=Nt#|**m0Iknu%1I2C~if0d2mD*ltVV4$Ljk#0tSpa^d#8nRK5yb*fn4}?)|T4R=}{_hU>Y>D8iUZf~iE6Uam|XT$=L>Jy#}TtRjQR3u?Tk zvKUvO#qYEoWoCe^Mazn#dl{p1MQwj2t7O%z)uNfXXj-k7{h>;Vm8$cwIKF+fh#|0# zCeX?H_UpFKK62-G50{hC%?xsXFGCQE2_>@DxMgvrU84qw_~iF)=Kg$ab6~ zBPhmXl2JYq{tfJn#vs`?ePp=5!!u*lSX5q88Sju*cs!Af9ac8SJLw9k9z;+5SEVFGdvAgvP|d1YG4HaN)QA&*Oz50**<@8aC=8* zcV$QJoXYt{M49WpcZtQG_2PM#nC5w58{{h(x2!(;e~2r%s&&DuBDGI))47_InyFw} z$-FdT<$~iP+C$J9*?Ur0j_%4Spow)B5JUr3fvQT?57dCCSLWd|OTmP435vu0`_%dV zzW3Gc_dU;FYZS@8eb`W!>Al->UQTv^zA)**c0Jo!q~X7^_=zyJC{$LRJVNj{^HgvAYJ z@e6eZ8kXLcG2eZ-EECKpZgxfP?T{UIs=K^uSl$I^k!(^ila;D16aLHn1 zqk0Wk1Sww&Gb$1_%s=8nFczRUTcF8@%c%VD9lk_glL|+@Sy6D8N~szx604FrxylUh2RT~{5VyJ)uHee`o_z9;;Wi=_x3-Rw3i-Da}RhiXFO5a&c4yj6d zl`_c}W-Age@rFZjg7Jk_ zMzQ6ml;C3S_&Sr=_aoeus*;qc1ga;rQn#GzZ52bDT10Bhq;+e9j2wzSDe;Dplgjxt zRg9tB!#()l@Dy+lIbr=;Ja>KpbfzI9=N3*XThe%?I6e`47QKwDg@~{zEM&N3FqSHm zw6yLVIP^QTICYy&KN2qu)E*Phc&`l1&?Di?z+%=8Xe<2(egE=);=2SnLX-bGKe4%d z7)0h>jGT9_@BSBMN~<)r&X{~0bPilzE~QRlMxKmYSiWlYLKW;RYumf5!@)X~OAOct zt`Lo~u-M}%7q)pK=RrEs&Jio3F?n!4t}<#rQ=AWWg}4Xj<9jm^awWB2=Zi=Zr|GcPsH!F$=VUSY>aB4YQ>xIOPl@??%hta_`XvtA$gfCj<_JeSFns8 zm<k{rz+v7fx^QSTaF|5_uy|l1iQ7`+Z6gOz5vtn zDsjHJ-c=`NfYADy$@l4tsY5YJP0@>iWS=!@Re^5+$!Txq`_vftRBa5A$B0sef$9(a z%6G)8@+mFZL7KQ9o!8%?o@LfPm7SVde_Ed*I|#+EywwfsObm6=dU>39+@YN%Qsj$z zjtB2nzaHDZmZhCi6e8_jjFn0r=pb-5`azL zt@a8D?&V}RU!&TM+%G$+-3&4Tp5?))y={X>kGAFT5EGH&7roZtCqQ^*w!LlrqsO=B z@lmpZrhobs9Lsbc+&*~pf4zfYlds3dF2|3ysdI4p1Wx}Eh+7XlhmGrb5U?PQVr$LL ziJEHUz=B*VG8Zj@LT#peK*j~hDw$fvl8veYBqO+%6`GDJBT7h+2}_ki9c`U$hbrSnMZ0(E z39{7~uIke02zb7W1fZ!N-ui;0R@tnT@+#D692m2IiOSfXDM3|Y6M7MT_zWwT>ce;EK`wb8?O4kE%c3xTU*)bH8Kj#)gHS1+r>UT85}v zRZ}jjw#3^rM9h4m0QY7JPAsc))~?+ysurFckyQ;_w~M(A5P}Cw`Y8&Wv2Y%tgakh= zv*~l>)AAWHEf%CxW3+6;n+p%%&a~RR5hk%)U0aVSg^C}22hm#f*as*+>@cOUfbkiQ z2QFisO=Eq?MJpAV2DyR5{E6x#xF4-HHeryFQQd&3IhzA5!L8f)&EPKCIF493S;|3r z1by6jEHnx{eQu<^I037^>ZMeycIB`gW0%E~sm1N6HR_R!w%sxde`O(j#Y?WcNBJv% zV-D#D_~t*G_H6#(^#hL0TbD1{;tAhHhXR7ozz%b4iDE;2Rx(V|paH^>lRE%Xj9YPb zb~r7Owi&Y*)Xev+r>bWCmR%cr9YdQd3m8a-85G~s=OWF>%0+9wM3qF(f?6YK5#Zmc zia$&FHs$_!P1n&5P}QUwG-@jR+|0xzC|P>Fh$t|2j0|p9@LEW$NF;n1ZMKq{t)mM(Co6MCq!X@ZiqZi9M4o{oEG*`d z)$_LOwRdmX(%reaGH)|+XJ>DYpr=Uk{*%mwL>W0EBRxP2Bg?ns71@gxRF-hj@XK0l zBfT59?jlhcVq8*L-cY2fC3-;yEJJD%MU0S%LCmo#1x~7`lVW$#*pD}ptfksX_R$i6 zLdZLT7vkhSKzo@OsK5g5uq;KlqH`&C)g&uIRhRLXoi^lgYCB{Imr(Uhn?omh{)+Pk+IrqhB3u_ejGh z!kDZ22qqXP;|+!eKLjpPLO^`_f##a$YAv}BB;S!OJ|T=@J!`TpoK4bK^$LY>ll{Ea z%@vQ^*ZJX_p5}X>yMOil7R7lU7;~s~r=BgEIdGxVbeByey&%pK#^=PjmjBBDDCf@z zDV5yke&}Ljf5Y~MO8flEWqIz^Gl+nOdPhczHWopf4wN~qfIbm-3Ht@T@^Z_|rNtW< z3&q48aX|aUJm^Fqp;E;l0$3YVNy(**fX05e=s+@f<+2sa9VateuVPNhs zK!dR3y${{@8iOME1-q8)Ubyo<>CTMqo-L9_-nnCY@#fsg|NAeSbB(xony|2%A1>K1 zXPuwjH}jpDFHZhH`)W!%jkz|k=lwh5#En0oBlG2n#+hXG;#=^bF+el=F*BzHWggW z0uil3Qx%x{_JO(*5LwXT_NEe)_o*(@{@qSr^}VdlVb_c-x{`o*Z+{ii|BaZjry%v}kifcZYq@Jp8~Hm%ZWs>g~7s57@C-hEa)h zQiwX0=)(V^ARWJ;zk9*#+4JVj4vtt$yKQqv_vTL>A8)VBnNcubMq)Ed0l-A@DiaEv zZy5jh@$ZMkI6IOamQk6I5?@)6gWbz=nzL>5$`_=}p0RmkIZSI4Ynd{qq~=7zehhMq zG<3?sUGAjKB^$f#oo!q8A8#u!AYcP^14#`!tZ+oqctml+SsA1#H}7T>t@uRUP-(e}IQURL#S%n_%s+P(9q5qdP=nWw z$3mycV_)vryW2B+W5+#TLB3wGd8=oM2s18U-ms#1IHXpmN2Gzts3G>$$~;oxj1Lxt+%>0;2yI$6)3jNyy@*d_j*X~fBAafn+K?D z02@qD=!pVx4BX{Dr?npOELoT$>pg)g9%H|`;^X~GzHs2?DP5paVa+)v&wAVQ$G6|# zcT#ocj-eBmTW0(B+O%iQV(Mty(CA=E`t%am9tF}lVmi8C;fIET0vVBd?e?9Z*NiDu zCtge@n3@WO302_{BbXYYo$O>;dMCfhYbDM)nv+5>0l? zHh)27x%;UnS3E_iNH}xK-1Ov%CPzv^T83x;B4gXu?QQO7o?h{cBmT?>8EKyVON`sM zww?Y!&*T>A=9N#cSm_w)-9PM^wb5Aq!%2^ieK6d=cZ7n%Ppx7mKU05X$0v2yp)N#a&2nome?lAPvQ%Cjz*Aw#aXe#ao=|26X$ zulKxl5KJsi4W|NgQb}dO!bK`EvjjqR0}WTVss@@7qb&QrNVB@Vo7exB+Z+6Gmx^B* zAK&~+(-z0cy>Irv=V^gZ=Jc*4N4(QCw|3qGS3BYt42lr?+5q9*VouyJdn>e+zy_yz zrEPi5@-5Q9yn@6es{DXeF~Ct;;cNfgMqe?#F!;Vot+lzPSiSDiyhr<)bL>uD;T!9q zrrCwMQRdSj#|LXFk>M$efkU_ABLPZoP z)RA7oqYla-kl-^wGKxkZ3-Dddz z`uP{G9nu^@9g}2LsZmE`7(JollDiI5ZB3;{9qT}i))NKc4(z8wg7KaG@}GZ)uiDYM zyn(t1B89a@d8=_gcF1dyiP?O=2ceh>f?9;(6oml$`jt)2!3IZ@kXHS;n+m|7*2sPK z8ahE|YN-_0?m?o6%q}RfSOcLbj@WRtWOT3nn~_0Av{NL`TQH>R9(T!z*v*n6kc04= zk}*^wB=DRP@271hy`M$>Jf>5Vth0k8w-N;`1!+^v(WR;zwB}Os@}_;X56A~iMA!{F zMpy9F80g5F8)JjCQb+o>4noP;zbydF)L`s}EMv;1B;(AhJ;9&%vWt>ht&I4!N1Hq9 zo=-1ue=$K+B2^bH3J!ePHA3kv*6_iG7|CtFo3Sq#@H|G7C{?t^eo<3_8XI%rY3s_lH40^OW`GMU_Th>MP7dFz8Av;6qEMD>(!CZ>Om%(-l~l;==tY&U6*VT4zx!&xeUt zRi%~b(%!z%rOi>Vek{ULWkQ)#VPBmo%YqJlz-cZE_L8$WC#H)FNswwilx$r@R#LqP z3BEYMi_6qUV=u_nB6A)&-9=J;&n2*2|Lv{1s{LABAN$jD%-o#x(-&GPl&_1`sm5OX zaI9XfC#R9oJ$ApIf}*PqoP_7p7h+Jr0?Uc9(Ki!*?8yQZ$x2XcC9?iXn0FUNPl! z5jj~{h85M6;7G=zp2 zGn_zv^y0*l95YA*Q$-gNxub)28EsPbZ@5@lf z1y|mZdYk7y5n+^Z{sWA+Z%sp4f4}wA0*kz!chk&|f7IR7<#?z4>EyB2&ZMsepxyyitYTq2=3V?k_{2K>=nU0K_9vkU3wW}!YaEo0J&f# z86c>>ZZVJ~qBCHE5xBf(6Bn6(^VEi?*1JD{dDlDp9mCxvbSso+n;*aXuBJO2w-hz6 zK~1kpEVJ$2((!7)W294kPNFUBooW3y|G~mk^npM!?W(ax)hGz@H8`(48G`0W<)`QB z(@mgI<5fd=QHDO|^li{SGqewBwg8)kQk-z{hbKKlRGX3SZRFP=LP4u~*pCUYpXYLx zo%(=BKR~|4pdB#bc?6RUGitCKsSByfV@zzrYtI6qiMazM0u66D!IV_cKRg6bjQz<; zQ&gE`L`2f9+Di-gGnZL7^(XJgw)qPvq*DYTtxnG#f{2l&r^bU{(=W-acWD=_@MgH1 zn^!bH^UR8;9a_Bi=@nX+M=p+%2lV{q`bt0OETaMg7^4Qnp zFXr3-#W#I5*A<9h3BWfrHSjaradZs)(A{`9{K1~h#U;pHEsiLcYc+hFc7B?HwX_Rh zp^NYbHxe>QHm!r+75OADr-_&yUy8Fl*}zRP_lRi@Jb;)TU$EsG5vsPtIT%1^wgmTK zORf3RtaUAv&s&6jFXYz6Xlds05PM`#jyB%)P=3}UsgBF8TDO^9CJOsLc6pdxa>jPa zV~RA&#d}Eu;e={$b;2`ybHasrSN$@T9#~x>({}0ick_5>(jThr z`0#LBJcfDZ-7?|QDQ<7)O53IXx<|x|%f~()3VwQdrHsEUhDY&Q)Mfvw{NdA~v12d4 zN5)?oLln_{tt(uXdrKGCpg`3@J!)z53|q-QY``#ND^Fs_a@HA?G4StP`3QRt3#=xLXppt(t9nMpe%4yIx22At+-8&%xN0Oi~CY!F4WzLde< zs;9flO{)AN6$;0|LbIMt5WhpKL+{zFRZy|EQse4&coE4OAOReroeF-*DZZqKtPWDK zPUB(LF7X6fFD8hb#bUV?Ym*E`^ARg^IXNiJOb5~ba{M-wLMf6B(N94~H|wX!GU!^X zTL40|pmL1&+N3)bg_x{9CGom@7rt_wNsr^rI&Dqx3YX&i?bHY~Es~U5kJGo4ub;=a z$Ek0(@a?3w47<2Gw7puR<|#rGydW{)8B)df5iA$Y?k(ivg#~)0Zt38wkT7y+7$MD| zp?iS*XAid30*UzseDZC&vph)dZf%3Vn=RgPXeK1Nck1a%;DoCt{L-hxOtymRw9Z$C zT2*Pne9>@4TOlJPUIA@-oP5Z^&QmGUrDhBv*mQ9dO0}qM?NAhB%~{UkwM{Q| z=uL7!x9B}VA=p0gk#5kQV3=OC`)h>x^kYgB09jItkFO||C{zZrkry<1kN60lk?(Q( z=g}|=#SN$T3zfS~kJ1oG;d$cCQvwWeNe!AF^)NLicQoWJ$P@K2^Z*RQ#Ca~MyE&1Q zXZop2Qb4OhEzRSS5gNWjrXU^E-i#%fBr5D9h;8uWqvab7GJ) z_7m6><20vnX_U6Xq(<#eFTj6TjaoF!4~bKZgg&>{3Q_{aMvM5jYXJ=^=33g-wX8!- zfxIKg39gdVmD_~I87T++LB@~9s-akj>rB||dZ?~d9+;5Sf3IW&#kjQ|9S)y?v??=> z(quT;N}FUSAy*Evh{Huqt9Os3WuhpplfyFf_9vQS1!rlpq7eev93# z0)}OZ7N=+kRh|@uiWzX;y4O zjCmkW7=@bS@{o-*SQZk;G#9lLA{L;9F+hmZWDkAI5MpvtQ~)2x53z@Pl4X9Vw7&2f zJGMBJDJ;?l*HUu68AF^Y+P9L=ZL35S@yw?WO7oDs8V%cr#b1nm(r1h1A)}rt4$*gj zZ>G0nu~94{lN9`lF=7>=zuhFuek-OjlWuhoGCH|KsUDeXN@#j|KC6#T;am+e|XS={@N`=ro=6!#jw6I7tIu zIFoS9S;MR4Db+t=9rhn*jD`j#5>7m|1hLbB7ef1OX&F|HpRx>kvkuQpHxzk-&S1A_ z9TLX?EzK1M_^GWKvJM3d=8&OC2l&&Q)kk#(ecLYMY6g#GwG45v8kI7PGC4&;xpAly zT%LMge7WVzC~whP=P6y^7K3mu6IF|jHdq=)MEx=mqRIivfEBY)OiJi5rWL{FlvNH~ zhQ!rtRjEeAYMHoJ#%#7A-+(6LXG)e%)#|AVHkk4m}<(4Q{bRRrj6pBgED~b>NPr38|_wUe~{zpFp|3Li;ve-ZT2sVABB&4YT z)Fbj>!{suwB+X_N?-$!M79BvcIYpq*$d0b9LC+lT${0xY50AeZ@# z26wcGSWDIj$E|RYY)s-9JQGxxQHIK+9895-lmLnPzp6^T75s&ainauyDI>xT`Qm`S z2YE@*E+_;WP1+)-k@#|r3AU=N)hUJ`+!9lq#!{zchfU9C5wQ?uY$C}V8+b>4?|TG| zLzDJPr*A;NUBM$N?fne|@pwH>eBq+>YpSZe))qUhYx=C{D4&(kr^NLTHBpKs#Jekl zEg{AvGkmGCC6KB_vFo|A!?+9eF!tuUwN0pxAr*(?975?>O^SOfT#ogF9jm(?Lv8b@ z`qEp>1R#+aGK(c$d%$RU7 zwHN7D^RAYwYA+vlX_~gybbaD|pk7z7U+{$gvo9_-27ZqJ@%__3ejOIc&>jU*0fABD z9#xB|pKu}xuBZzaYcWQVgtyb8#fUhQ+~bY5ZTWu76fsNBH0mCz_9r_{Uq^{o)WIfj zu6mOU1Ykx?^(AuoF_cMQvN#9DNf)Sd<#Miiw&$7jb+E)lj#j;0ob$RoM_i{@V;zs9 zE#b61$4D&#lh}OkeeyD7R@ZbG#oMi@5@d%05S?oH=BxO{%t#Pwm;6=9*G)+$zps@?i+yU!cfGCpKn$n_zGAqztu3wb@&R&@s|>3R8w@WQzBBlYA;xLOON|-ES;p&)bB%W* zU0-WlWL!dL>1b1o=@FCH{DAo@^B>HAGJj|Ohb0{H&|Q|7Ew5TWuzY0s*z%P{n`oLi zed3K1ADFm&;tLahJMp!NA5Q#i;{J($p7_nglM|(Ng4Jq0kH&1V)(fmFtuI-(SYNY# zY~5@9kI)IBlR~cu%?iyAy(x5Y=r2Q`3*8d>R_Obo-Jzd^ei?co^i2e{%QE(vqR6m_3YBKA36Kf*?PqN5f4TjiZ~u|Dk5->@tle0 zM4ofYIZvK*^c;Ea+;iVN_mgw?oELH4z2|*+-Y4e`ocHBbj^kQNvN+O-h)QJL%3zHIo)j`sJi|Cw(;OKcbz{v!Wk~ULQR)dGh3$ zlkc8<-{h9bZ%lr7^6w`9(SEMoWuIcd$ev-p+WvFGjPnklbO>7CL)OH}3S=I-wpxV_4ZtT&Z?GAhdzDlW6ibyvDV zwMOFRyR4>AW$HskHPpi_LS5Y5Vj{2#F;|Nw0^2l8>`Q}7M2mw_xp+u+t&)o^)GOrT zG6to+PvXS;vdy7U3tgeMO)lGNs|Xo3&Kj<)a+yQb>21f&C)c0s7EYB{jyow^-3?TN zh_IszX6KdE$e0EXh+#OgEb*qKg|mugxkXrxQ5Yz7k3MM+r{aI#%hpiUX5%VrXi$wi zRjUf>QH}23J2EnBZxZR{l$8CmM?IkcqU5A~&@-8l-SQ7)9@hVgr+3fFg{5}^s(MbfiPHD(j9RZ#geQ~pzRTvMuJ zJLEa){_YH;xRrU3dGy5tNSIh%cUhv);Y*kCBjkx*l`N8 zsD@Q(QqST`ZWtwC`Q@%NcHRmggvtrAtVf$9i-G#MdT=Sl5~5hEdT*hr7ji0 zww@mkSrP`(4NVuzYkuJtKSQc325|^x2QaaLw(=is`ZG!@+wGxOShc+-#9c1b_WG-h zkYN1R`wU)}H%^rU`BVK_MqHR}_#WK5WCl?P-U&BrS8}JQ1`3>VdfSbQXftREHF?9m;q>}Q3`7PZjo~WR|KhZcAw|W>GM)5j zDWDIS755XK5kynlQdejoE^xp{<}=jkJIJrI5zeO*#no=at%uRPC_XP%wKNDJjH)H5 zG4L;!I+PTsF>jL9`+s6&UX3rz$s(sP)X8vuFxoE zCZ!+>saN%^i%p@b0uW~R78x0;zlv~Er~H7~sqZ61RU=8+5Og0~<-t(LzZfQn7c$Xf z7VxnPDdL-nfDXRrqWek4#V9zAb5kK@k&RpBbT#ibWPm1z5!Pm4QaZu5=r^rs8UzDS zy2w}?RA9P|ik2Ofav)Ehvdb=bHd)|cn+PG|L{_hRwMpJ|3;-uwyy^&?5*4pmy*7yL z;<5GA2j3@NbdbnN=XtF2gzmK}dQ_&B0F4aTHqFx6xpw_7x4y&Qs$9_jkG*$+Z=y;c zuqVmPByCDTX`#|vgaQEy1gKmql$)R+Rz*QT908#!!Uz=+5To1*3vE_WP!NWTf{233 zt_X-#s-oh`DgrBrQCY$D#tJ%VCvCpxBovC^ZP)!i_x~;VJ(COR4iD5RE;dUkuu`Y$Q zImw~6vq`tGD|B9ikjHw#+-M!Q##onTv7KDbn5u^6%xAChpUmj;o_e^m{a?s5i&XR7 z>#W)41nbMx@Qv1naCT)uW6Z;IL~~gW63rTK&YhnL=5@ThKb1+E%gI1@KZUpk zWmJ-xm>kXt;7h$7PH2E3y_p5OClIc_$3z ziyO5S(0A*+b9`$iEkn-SZ2nW~w&Tdu3~Q8X^Wk}^_ zI>9IwFotl_3q*V^k3}9$spZ9H;2WNNrtcdXJ76tt%9D?3qa%)wFE3qAevk3Az+|qd zav>KaZZkcGYibm`t%ez&ZU(lcXSmJM^h#Fh%&*#&%pH4KUG_0)<}V|^%si{EHNg51 z1smPob@I07ZZgZeDzg-Gp|h$WG*(&StPoGUC9OZ7Cni@6Cb@whD`Gt!_qB;Fq%8dzC)zpyCsh|rf8Dwd(Tt(+sw9_O^na8D$AjakNxp5vs@!ZcIj z|N2{XnAmVzp=c&cw@^=};SL`R9S-wwa~BL}R&sfYo1FDSGiJEM)5L@GFGZNy>}RGl zWlh~G7PgsL%fI;4tkZxPCW&pLg58T2%b%0MKz!tjcNn7*KI+r1b<=)U3hQ0HpR%Rq zc)u2{Tl6G&n$q;0Bji^hAc)j>xCe^c941mI_|-Y(6U_iCqUik7)7^KfBhuYp(V?5h zlMAFWyJA}?R54a;R}ZhP@`x_xHYY^?ODZ?aE3p&gdQ|F(P)d1Hc^&ISC@Fk2ll^rm z;tp!`34G=9Y2v=9Cbw@$eiM_GjDpQcTQK8rX1OPgdmwe9GDFGf(fwA#+!>Yd=A9v~A{q_R{+>~*bdFHn`BvITNNiuT;|{a`s?2W|nZ}yu zwz@D58CywYZLQC6S1e_50E=IUGUc*c!c@4Ofh4x6yX!rXzT7%-cY3I=vW5kfIQ{vA zpSHwTjQS?M3XL(ATnLk@_Ru}tY2O`VVaogIiRz2#TVMLaq751I)7@$DM_IdJP2Qnc zmu>E}(~KA4G0zt`=_7)cdf_H|p}1!R+w2R&94(Gvj$-+)$QBz)il*M)DU7D|YU0HB z5H|`});O7U`ch$73e^c$DDG1?S{Ff@0xazU5z=*=CH%6B*<7Z4tUB&wZ|BZl@4?Pr ze|_-a*I#!&=w+E9_p-}!GZ#~$GTr1?9#9igsaGEgek`KZ5zVc;b<)F;4vqwXr+2~k zaUZC6fP4CWWiAcPUy->gv{?!593@)nv(RT@P{LE(p$nO2!%o6DVA!ZOGZX0o6606F z4jN}z+PC`|0^Q~<xE7MkBVF9wXfP7PL<0pP1K*mc z%&cMV*OVKeX_+BMv-=qi*&J-iAMvfM@nZFvZz8qG{VqjO1|!HzGu)wG9g@)&Bb8`<#;Mn(TZg{Fpzmy9;AdNRu#RqH zg+JMRDV_+<`Oofam{IIx@!{svX)H+t}$gBaF5oYI|dRRdz)-PXzIBBO~eIX$;e>ND7@` z)e*DlOrwQPkjLT#cSpdROm18ym4=LL|w3-E^_VmT`L2DkgWBQ5E<4qL4V%tP97oTbfxSb}Mqb z)D~9OBHV;UbZ3`XBFc*U%t94*vtmwn9y~ZOOh`$GYd##F{8nx^y7cOsV7^` z%DIUG6R$JpnvE=doi)W3{tLT#cdHYu>C}+lnlqP;unV|ypGlyby>_oLXw5SZW0UxC z*IQ(;F}MY@pylieE~R1TiQ7ms5z&r;zrLBX0QD-RTP?B%Sn-y#NBlSO?s7w{kYSo= zG8XByo|(h}a57&vGY^<7(|v-laUbi$hcc_aX{s_maumtk6UrfQelRpPB<}69n`xTR z#40wa&6tY8ezUbwEZ*!$>*7C%C`_g13_ou^M>PJXn=>jCxV1(tXXuqJZ1AiMWi~KN zyP2E1h=5E${lc33j5uszi-UX(|5d12xdbgaSMsGFBq-U z#8kqvo4U?s1yq_Xo|1+_#btOxK{v4bb+8)x!LLnAzTB ztUKnLQ$G5Xg1Fx6f>&ReWoqRq%nQGV(QeB7u!=G&J$$cMY;AlJ(#fP{H!ERe2FZgG zZf&&B@}$OABHlpG{5+8&CuDq2Q*JZu__BNG*>2}8@l(>5hqJH=GAj3%7n#>!$cwag zF0`IkSbtga+cC>|%v+KE`<|Hx6NGgW2){ zc6Zk{TL=LY9%oKwBDt8$pH)M>D!MA~tlP9|N9vk2b7m~ce90_S*7O_tKtIlZ8A~(s zGOgW}!>nE9!xVF5o0er(N@~BMGj8q2&OcpwV9}E$t5RRzG_lXR%urrcSH=B!dfExL z0;i|BL(IQ1-;s7=vshb^L`iulX|*<*%Y+v*bCFtN&VLK}?`7IuX0Pv$6X9SX9^Dyp zjb#mjKVZqdNp#@8A6v(441 zDBI1}rtn&!qgNCDLJM6%NDLDz{{gkp^ItR)IoUZQgXBTZ)fY{{Bu%?Ujpz@d^^DTy zsLG?}Hue#i1@t-Y%VHIT*UvDwr;D9x@)yr#F0!~C=t9{lV6YY*T?<-t6e-^LDF#}0 zG?BbJacG)yBANBl6$#v4<1ml@TlivQ zRkKIY2PDi*eRs>)_FrXMb{z=&=DN(=eA%X9KdrlEWqb{az9(WaHcKS+wpH> z&GAa4AK+Pd?$f45^LLRw=LFfimc+7mO=IUrINO{fR_%xGQ*W~JQR@%Cr-WWJZ(wO_ zHg;w~Ja=dI<5HQEaAK}td2B3_J1E_aPrG>$3Ccb)+mHw`)$C@r{n%`Oh&Gx`;Er4j z#PP&h;9Nvim{qxiX^Jx{Jp6WIR=Syjv%=kzxXZ`lr!FDXIHkc`+yv}&K9DS{7u(Ag zvvNx01MzccF$v!^zwe7qm|w3jog1go`Jasv zV?s1wO-W@dvLLe%GCQ20g!2ee{6md?F4kahpFX9$6;ZJLIDag23FTn}{Ui1{j(eBa zzc}*xOQvsm=6esA39Fu5h4;W7AWM3MXli*O**p3ZXvWrbT8;TQy9EYAAqzRoUL!L_ zWcp7}nX|1cLM*H(HK!q#7wCXl7Q(r!lrENI( zYUpb=#3^R#iz^s7Q=c>^_hB1XTdTSCkPij6o?wm2-pO&+s;jJ(w_9~DAnnjrGwq+| z&Zo@Dj2KI~VNNuBomnG%9c{I=3a;T|k1*?N?TV+CuVV&*JDG4pV{Tu?k|3>8SH#;* z6$_)G>4B92Wd_S`$^(k9w1&mr4i<06k}SD^oZS-L$IYs)%y=uQqJNKcLIbt-h!{Wu z=`34qOi%kZn;1bfAHh^wdro?qLh@RPh(nb}L><;v;47D^p%YaFmD$|JhB@g_8K0Tk zsyy6R+`|oYxLvh}!1x{MTG4VUH5(c2s6XkeyK%RhU43=GK05}zg*1idlVYMH z7ECCEC@6lzQY$;OF{C#LkwrhW5gTG@GzP{OSZ2{NJfE9$io28(O%8jQ{brGw2#c&$ zPb&sC6LC}YS>|zccuZCkqOzJ$Evsb7@R3%EoV}r}^k`N!^P>1f6wdd(38=&`Mz5rO zSHR0S?)WTJ4?@3LE%#O}BzmwgmZ73DrDCCJGM;1Jgtwt&SUggdW!@8CkriD%vWJ8* z?ljpbXFqr^5eu049$N^q$p)tztWRaDl=PXz3TQMw{=ZzJ#vScP`$Cvn!?T6a!%}dcGnh4LEw$FC1#Pcs*d@B^02c)Jn@#m z8=v#PRWk`!@Xjh^YK^^6gHe5A$3?D{U0xuT{uxYUxLLL~853vPd#rnYi|qPWSead{ z%Tu|L{ngaOhg;I!sbPaT8853P)0m0#hBmpKW+gf0x>%1BtG%d+)h?Hav)Qqm)!++j zIDWL{ZDDonm&tM@5!w%>Ocm)23q?9Z^GJHdK*nZnk;cX(g3X(vu2UV;_g=;lhMm&T z%H;B*@Sm;i6$$0@2vk(%uwt+x!P-=nAYPZxBjB=~*JAjk+_AjHv`CJ)Y>|Aih=9iBZ zp-D^%x>UAV_~x#A<}4BB?E?Ms1*}=?Z{;+yvadFu6#cvqdnup3y%0YzU#zc}L~3RN z=h3i#7D2BltvcmdEQ}`VzI%!2*c zy%fk{{)H)K>M(9Z^lBicm=DdP@eqOXfedQg1(U>%BA}`ec44nMiyqm=@)0-AwzzQy z-%4uDGHqt`2!Xv2wGu|TF*`eQC58PjTU5eCz)a`d$KROyw73s%&T-PJWiD~Q$P{Q) z&s2IVnS*4jpufC76XSZ7ue`5N<%kv6HH|w8EqCG=m4B(AiFu(WiOISz&1r5*9-Ue% zU^sg|ohi;l5d_X$Zzdl#N2D@sjFpqFh9)Cyex@Jx)4si|{6*Zq1VgX9l!WVRAHa+( zXXk5HC36~~g)^Mt^_7E(REpbWbBAvpBAfYGw4WjLP$g6HgURlY8)9@bTR!=s*(Ia$ zFRbb@S(H~u6v9g&fjBA^I@4Lnv}TjXkF743NL0!Ds%hbDQXV-Id&c71SmK3>8}G}y zi5~VA&7RfB^7YJ!3agkJ|H+$_xQ~Ch!)&->ZP9FYV-9oPf5)HzLq!oKsBhh!iOo)k zVmFGY6vv9&Z=gf1jyFn8jysVD&K03X9Lel<`>L_ zdPJ*3M!A;-0+pnnFZY@WYG`StS8-P)&{PRDRe`X&au~SF3$98pchSRLSEq|kCvF3A zjF`+Z50W8g4Fh~w-)$W5|8yrpt4^(D=Xs$OW#V)Ae@fA=0?oy za$k9?a$hCN+RU!pS-!K9hr(3nW(ruI7OoTWhK=i3;gO=6ew_ zLGfJ-7UI@hL;wy}7N#kPKLK(+tWG)=i|5mSSgGEHh3bqd)r;Eq#n!aDnaa=S1}NeN z<=o!NVlQHNQ>xpn5NY};aQl^y-g@T?;r1VdO86u$jb^OQFdU7ec;;s(Y$4yf7Rlm!nvHzu4^WX7d5Ja76?Tl&==U zx0|y~cas0QV{5V!CQxY?$G9x}Y+TlFi zxQbq6He}EjCbxspgjhRsVNN`l!3alKvYIHQ!A7i^6d)TL7HhANnBPj{ZFm#2!?{>( zBtXul9>e&{)wkwwvxuDJvb1dUnfZcxR4C#o(OguhD4;GzXdUfZ_Sm*3_NN|sP9*1a z*Q=4yw2wO#laeWA_K>70K`u=U8_YC@v=GY)n4;re2IZS6rtTK2H^Qqck1A8envna< zUGZx9v5K%_=3}1cS)(fQSb4CPiPueGmpjBQ6A0erV5d%IR*<{j5l%#9R3wq|Rr!*x zm{#GZfK)omH%OzFh@R~z63A#|t8kxAd*r6AMgBZCfw{?&u^Us+Z?1MzLD)b9guA$t zD~njSP7==KO0sm7Z$qt5afX?$Q4%GVa7t1Fh<|qJN$A377fVlZ++a(VY%Js~;yBN? zh~vXHgX2ou`yBV#j&uCZ_C3dty}M+y_q6xrc$s}5$7}63avW)YnBx-r5{}F4+c=im zU*TA0f0g5#_P02`ZQsZ714j?Z=IHIXlw*I#?HmgolQ>Rs%;7lK@h6VYJ2rFN;&_MS zyN>-F4>%5R{Lt|s$B!Hzas1R_ax8aP93zg1WRq>O%+V>kIL6EI924d9IVQ^)950j` za?FymIC^ESLhdQ|KKJe}ix@+^*XKKl!TnVlOj&)siIkKjeV?9?r{x`*S0k1P%T#>7xi}rPSUClUVyIOH<>uSrf zgR29_?k?Ke)!Ri|yDoEG#&Lja0B0ED8p`o{*Y$id+;s!5Z*ozquA5!dr|WhX{nK@) zi~4j;c2S?MyIgnidb*3g;kws#FRufx<-A_yqHbAWypH207yZPw#kGawb{F;Fddc+? z-+bBiGOx>AWxW22i@xnT?+-8kV^$rJq_4*b73cl2A5%Vz)kgp==GJjY9l zREO`{r6v%^_uSG$l1o}4?UNQtf0K?%Z`tB(bEE@|MPJy4*uJuvwrwm9?`3=4@up*! zZKvY{xv}jnrJhpHUf+4Qv)G;@da1oRy>zj?h3KF5HqJ+#%j|8PE1fItInKwO&)P5I z9wJ-qJw%_hU&eCq5%z(iP3_mX-gE7-U(2HKefGhw1Fi%1A+8TyAKHh;WyEFJhsCi9 z*?v8X#hciN$9dzt_8a1|tY`xTFPD!H!W_O z{pPsoantR$aE&gAFQLxkCrGj+DLD)F{(tc(?h$(|sQTe!-mV2z^LG94F{EtBgzCTf zmCxgT>ie~}*VY6k;>Fd}dd2&7bn9_RF z&OB$mb!Wc+v3Fxie^~Fo?tQ)e)sLV5yW$=Tj@55ozqvRrIJV%}yj>-U^>b5}l_Xx! z_ku%$lEen#2H_Hx+6sQ%BlZ63N18n1aN7B4`Dv@uicdXOm&{JvlD4H}wsn&=X#XvJ^PXIrueMUw6p)GWN)8$&-}EwVeEZz!`Nfq zma`wlfD$c3-DA|kb zt?!U0WVyqAyh8fLqr+z%6LSC2eRJ$xL6g%pQvFJltWU$!CG9i0pvezgv1IR<(&A9x z?7rD2Yd+QvKXdFmuQ=5I*su7DhC{TP-;UO8!hi__CWtbM=bwIDzJ0)i%lC-yh~rtk z;*8!hpeRes*I`MR>$W8RjM#n*jbvbbdNkfbvo z(a*FoN#eQYV@Si(?}ljn9hxDEIL02I4Ebbe&d}M{=W7k;?b32#-H5{@d?kq`i6cH3 z(LOqQjziCk9Z@=hu~^hu@i<kR3ymu197qjyA%> zJ@Y z^a)!z|1KTje?I5&4z~B*(r(+s_N#5Z?bqPnOZLI`;dZ-SvyZUH*+`U$0 zc=~(oz3lt#A^Y8S%O2)^l_TD6I1(H^>`&q2_q88(T;`ZyKkB%{G0AbBW3pqNqpoAU z;~B>#`1o(&|G$k-e}g0F_{5<*!m`aVQI=)JahLGo9aHh*6C8Kr#V0!a@_BNa<6ivt zs~rpEvGOd(i}D=#MaMq5RDO?d?3EA74dst`G{f)yOl~fJE`KSvl)vWDMm{G0Q*J9C z=aD0S$KxV7%%iDh-rWIZw$@JaP}EjnY=` zt>h>0xmD5S>y(MgMEM3~iZV^sl0JKdAg& z`Mdm(^11Sbyhu5&rpS+|scME?sy0>|%Vla)wW<87+DvULzovFnyUV-P%hb!{57Ytb z74iY~TJ>7_Lv^S+R6eAxQJ<7QQn#zG$cNQ^>WA{@+->Ne@-e2RP5E1=;#B41&U>Br z%HKI>I%mn>i->_7B4RL4E_W6?i{%REV&`JnaxQT$k;Beq&Si3?h#1IKB4Qv%oKHJ9 zC^qMxozE){=N9KyMZqt7LvcFaa=xY17SRBujw{KPq}0X7Pf?PDcdsP7TDn>(^@K04 zq~OcpItUN``B+YqXLn{CCA8{5z$gYqo2)(#W;cwN%Lxe!9{a z4}Fv3b!~QSR+|^`H7wSEleu?9)K>Q@8Y*6qH_<{L&keZvT^{iNCc6 zCBOY+Npp;l_*+SHDv?5`8Yz@gf62F%CnQ~22hYL_um!flHc3}IN`AEybcQaF3l~FI z=mvSv9j>56zp!j}ms7R7@Md>Pn(e}qSFnfY_a)sim(t${^Z37Hsk@{}J)kG_g5Gcm z^nt!G5Uzo1VGs<4>tF~Bg<)_#42K&)JZ^*$FcL<=O)wh9Kmptgx4^A17H)%aFdlTc z9VWm;D1ACAE1v`!+_hB|N_oDX#&36h~6)Q1$f02)9lq(M4lKqh$LLTCt$APX8p z6KD!vXa>!p1!O}@Xa%jo2W_A&w1XUI4;`Q*bb`*%1#;nH=nCCn65I)sfp)V^0ou+s z4W`3A;D>wRK9~Wtt8Et0u0)qKqTQNpF3f|)^kU9y?+!hnC-j2eun-=EhhPzi?>-EV zz%p142CRUUunJbgV?f`C$~o;r3|zDCg16x>@D9*kcG}DS9_#_y%)S@)!QbEmH~eRUmUV$A@20P&mNpoEXw6ANaq{Y#1q&WLJuHbpbx7m!e3nF>SnuxAE z#dusAF&H`bGji^ioF~8KKi9XUI<^B5!}cK@f{*yyt2oC*&NVxduRP3{y{0Oltmj#r zcXPz=RHRW(RT|}*5%IfbM*LF7InQu4XBmF#Of0qK^+I?E9;UPg&nsXRJPxAdPxASC zIQdKJ`iQPR1y92cD1%oc`l(+|Jm)X}MA=XMW%@b(GN4?|RSt*P83L3!K$!!SIY5~W z${e7~0m>Yp%+w6js2R!|pu7Rf8=$-a${V1(fm6TQ(DpucychPte)t=F0HO^JLXCE? z1$qB5d;*`sQTQCbj2LqK$#%M#5{!e1P_w;$P?OH5=?@zs#(%UV1|=~liSdJy7}Sw* zvQ3S%N^-8=W6%-?Eg{Y(`k_Hf7_@{zOBg3x!k{G#TEd_u3|hjV_tgBQ0Igxr8V0Rl z&>9A);pa_^PE&wQA`vwFn|tA#+zrN%l2J=2}<}Pr(M*2+zPK_!ImYp2yyK zf#)r-6-oiwcU;dH!Z@T1pk>!a0+Ot>;reRWP|{$#D-y8Rhnph0eM!Wye+$PWhQk%n z9dR&|*Ry~cbj*Pwm<#jZ4=^7VKrxg+0R9N~12yiT#vKm=weDC1ivc-sJOcclV<|ie z%V0SeK&v=b0xjcM4eKb!W}g27@4#;OE4&Nu!5%;=97u%≻|_4x~a!qUFUE4~Tde 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 literal 0 HcmV?d00001 diff --git a/src/qt/res/fonts/Inter-VariableFont.ttf b/src/qt/res/fonts/Inter-VariableFont.ttf deleted file mode 100644 index 3b642151ed40c74ad4901d862a94b985ad57e8f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 805068 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 From f89608d2fd62ac148df9e831d65dbb692295bcb9 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 21:17:12 -0500 Subject: [PATCH 049/280] Abstract remaining GUI content DPI scaling overrides This replaces manual DPI scaling with the API that applies scaling based on the OS's base DPI to render correct sizes for macOS. --- src/qt/overviewpage.cpp | 6 +++--- src/qt/rpcconsole.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 0f01849c6b..e0261a8294 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -113,7 +113,7 @@ OverviewPage::OverviewPage(QWidget *parent) : currentUnconfirmedBalance(-1), currentImmatureBalance(-1) { - scaledDecorationSize = DECORATION_SIZE * this->logicalDpiX() / 96; + scaledDecorationSize = GRC::ScalePx(this, DECORATION_SIZE); txdelegate = new TxViewDelegate(this, scaledDecorationSize); @@ -125,13 +125,13 @@ OverviewPage::OverviewPage(QWidget *parent) : GRC::ScaleFontPointSize(ui->recentTransLabel, 15); // Override .ui default spacing to deal with various dpi displays. - int verticalSpacing = 7 * this->logicalDpiY() / 96; + int verticalSpacing = GRC::ScalePx(this, 7); ui->verticalLayout_10->setMargin(verticalSpacing); ui->walletGridLayout->setVerticalSpacing(verticalSpacing); ui->stakingGridLayout->setVerticalSpacing(verticalSpacing); ui->researcherGridLayout->setVerticalSpacing(verticalSpacing); - QRect verticalSpacerSpacing(0, 0, 20, 20 * this->logicalDpiY() / 96); + QRect verticalSpacerSpacing(0, 0, 20, GRC::ScalePx(this, 20)); ui->verticalSpacer->setGeometry(verticalSpacerSpacing); ui->researcherSectionVerticalSpacer->setGeometry(verticalSpacerSpacing); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index fda41eb75b..2df84bbd19 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -313,12 +313,12 @@ void RPCConsole::setClientModel(ClientModel *model) ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu); // Scale column widths by the logical DPI over 96.0 to deal with hires displays. - ui->peerWidget->setColumnWidth(PeerTableModel::NetNodeId, NETNODEID_COLUMN_WIDTH * logicalDpiX() / 96); - ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH * logicalDpiX() / 96); - ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH * logicalDpiX() / 96); - ui->peerWidget->setColumnWidth(PeerTableModel::Sent, SENT_COLUMN_WIDTH * logicalDpiX() / 96); - ui->peerWidget->setColumnWidth(PeerTableModel::Received, RECEIVED_COLUMN_WIDTH * logicalDpiX() / 96); - ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH * logicalDpiX() / 96); + ui->peerWidget->setColumnWidth(PeerTableModel::NetNodeId, GRC::ScalePx(this, NETNODEID_COLUMN_WIDTH)); + ui->peerWidget->setColumnWidth(PeerTableModel::Address, GRC::ScalePx(this, ADDRESS_COLUMN_WIDTH)); + ui->peerWidget->setColumnWidth(PeerTableModel::Ping, GRC::ScalePx(this, PING_COLUMN_WIDTH)); + ui->peerWidget->setColumnWidth(PeerTableModel::Sent, GRC::ScalePx(this, SENT_COLUMN_WIDTH)); + ui->peerWidget->setColumnWidth(PeerTableModel::Received, GRC::ScalePx(this, RECEIVED_COLUMN_WIDTH)); + ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, GRC::ScalePx(this, SUBVERSION_COLUMN_WIDTH)); ui->peerWidget->horizontalHeader()->setStretchLastSection(true); // Hide peerDetailWidget as initial state From efdc536d07a893e5e1064b243e54bbebaeabee08 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 18 Apr 2021 22:36:06 -0500 Subject: [PATCH 050/280] Work around amount unit dropdown Qt bug for macOS Qt cuts-off the macOS-style QComboBox pop-up list for controls with short items. This increases the width of the unit controls to avoid trucating the options. --- src/qt/bitcoinamountfield.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 8d32f56664..54f10e23b9 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -28,6 +28,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(amount); unit = new QValueComboBox(this); unit->setModel(new BitcoinUnits(this)); + unit->setMinimumWidth(80); layout->addWidget(unit); layout->addStretch(1); layout->setContentsMargins(0,0,0,0); From 9673fdac5ab0cf679bfbb3843e814e55bf3de527 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Mon, 19 Apr 2021 01:19:18 -0400 Subject: [PATCH 051/280] This gets the TrafficGraphWidget to respect the selected stylesheet. --- src/qt/trafficgraphwidget.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 6a2e685248..8089abacf6 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -1,5 +1,6 @@ #include "trafficgraphwidget.h" #include "clientmodel.h" +#include "optionsmodel.h" #include #include @@ -60,11 +61,27 @@ void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue &samples) void TrafficGraphWidget::paintEvent(QPaintEvent *) { QPainter painter(this); - painter.fillRect(rect(), Qt::lightGray); + + QColor background_color; + QColor axisCol; + + // This is inelegant, but the easiest way to get the traffic graph to respect the stylesheets. + // Qt does not provide an easy way to use stylesheets with the low level QPainter class. + if (this->clientModel->getOptionsModel()->getCurrentStyle() == "dark") + { + background_color.setRgb(26, 38, 50); + axisCol.setRgb(174, 180, 182); + } + else + { + background_color.setRgb(235, 236, 238); + axisCol.setRgb(88, 92, 107); + } + + painter.fillRect(rect(), background_color); if(fMax <= 0.0f) return; - QColor axisCol(Qt::black); int h = height() - YMARGIN * 2; painter.setPen(axisCol); painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h); From 1b6ea4d0e8649e96ea0577b91a1f7b98dd76cad9 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 19 Apr 2021 10:39:18 -0500 Subject: [PATCH 052/280] Fix squashed beacon wizard email field on macOS The email address field in the researcher wizard appeared very cramped on macOS. This change allows the field to expand like on other OSs. --- src/qt/forms/researcherwizardemailpage.ui | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/qt/forms/researcherwizardemailpage.ui b/src/qt/forms/researcherwizardemailpage.ui index 33a3e202a6..d8bc1ca1fe 100644 --- a/src/qt/forms/researcherwizardemailpage.ui +++ b/src/qt/forms/researcherwizardemailpage.ui @@ -44,6 +44,12 @@ + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + From 750c3d48716e3d1e4a156e8a9ecba24c20184d37 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 20 Apr 2021 14:27:04 -0500 Subject: [PATCH 053/280] Fix GUI RPC console auto-complete background color The background of the pop-up list for the RPC console's command input field auto-complete helper rendered with the operating system theme's base color and caused text visibility issues. This sets the colors of the wallet's themes for the background and fixes style cascades which prevented the color from filling the list. --- src/qt/res/stylesheets/dark_stylesheet.qss | 31 +++++++++++------- src/qt/res/stylesheets/light_stylesheet.qss | 35 +++++++++++++-------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 38488a6800..1a267c6b2e 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -56,6 +56,18 @@ QFrame[frameShape="5"] { border-left: 0.065em solid rgb(58, 70, 94); } +QAbstractItemView { + background-color: rgb(26, 38, 50); + alternate-background-color: rgb(23, 33, 43); +} + +QAbstractItemView::item:checked, +QAbstractItemView::item:hover, +QAbstractItemView::item:selected { + background-color: rgb(33, 44, 58); + color: white; +} + QMenu { border: 0.065em solid rgb(58, 70, 94); } @@ -218,6 +230,10 @@ QTreeWidget QScrollBar:vertical { border-bottom-left-radius: 0; } +QHeaderView { + background: transparent; +} + QHeaderView::section, QTableView QTableCornerButton::section { background-color: rgb(23, 30, 38); @@ -308,17 +324,6 @@ QComboBox QAbstractItemView { selection-color: white; } -QAbstractItemView { - alternate-background-color: rgb(23, 33, 43); -} - -QAbstractItemView::item:checked, -QAbstractItemView::item:hover, -QAbstractItemView::item:selected { - background-color: rgb(33, 44, 58); - color: white; -} - QPushButton { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(44, 160, 247), stop: 1 rgb(26, 145, 235)); border: 0.065em solid rgb(23, 137, 223); @@ -528,6 +533,10 @@ QStatusBar QToolTip { color: red; } +#listTransactions { + background: none; +} + /* coincontrol dialog*/ #coinControlFeaturesLabel{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index c5423e67c5..fb4f9a4a60 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -52,6 +52,20 @@ QFrame[frameShape="5"] { border-left: 0.065em solid rgb(234, 237, 237); } +QAbstractItemView { + background-color: white; + alternate-background-color: rgb(248, 248, 250); +} + +QAbstractItemView::item:checked, +QAbstractItemView::item:hover, +QAbstractItemView::item:selected, +QMenu::item:selected, +QMenuBar::item:selected { + background: rgb(241, 245, 247); + color: black; +} + QMenu { background-color: white; border: 0.065em solid rgb(224, 227, 227); @@ -217,6 +231,10 @@ QTreeWidget QScrollBar:vertical { border-bottom-left-radius: 0; } +QHeaderView { + background: transparent; +} + QHeaderView::section, QTableView QTableCornerButton::section { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 245, 249)); @@ -337,19 +355,6 @@ QComboBox QAbstractItemView { color: rgb(70, 73, 85); } -QAbstractItemView { - alternate-background-color: rgb(248, 248, 250); -} - -QAbstractItemView::item:checked, -QAbstractItemView::item:hover, -QAbstractItemView::item:selected, -QMenu::item:selected, -QMenuBar::item:selected { - background: rgb(241, 245, 247); - color: black; -} - QPushButton { padding: 0.2em 1.25em; color: rgb(97, 101, 118); @@ -537,6 +542,10 @@ QStatusBar .QFrame QLabel { color: red; } +#listTransactions { + background: none; +} + /* coincontrol dialog*/ #coinControlFeaturesLabel{ From c8c503c42e57427cd4d3f2659284533552ca3fd3 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 20 Apr 2021 14:57:44 -0500 Subject: [PATCH 054/280] Avoid reloading redundant GUI stylesheets This skips the application of theme stylesheets when saving the GUI options if the user doesn't change the theme. Reloading stylesheets can be expensive for a wallet with many transactions. --- src/qt/bitcoingui.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cb4b2d54ce..bb46a4ea26 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -279,6 +279,12 @@ std::string FromQString(QString qs) void BitcoinGUI::setOptionsStyleSheet(QString qssFileName) { + // Applying a stylesheet can be rather expensive on a wallet with many + // transactions. Avoid reloading styles if the theme didn't change: + if (qssFileName == sSheet) { + return; + } + // setting the style sheets for the app QFile qss(":/stylesheets/"+qssFileName); if (qss.open(QIODevice::ReadOnly)){ From 263ce49461875bc33e915358ff011bc62885dd51 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 20 Apr 2021 21:15:07 -0500 Subject: [PATCH 055/280] Fix "no beacon" GUI status The detection for a missing beacon private key overrides the status for when a CPID has no active beacon at all. --- src/qt/researcher/researchermodel.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 5708cf1d54..481411ea93 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -560,13 +560,12 @@ void ResearcherModel::updateBeacon() if (beacon_key_present) { beacon_status = MapAdvertiseBeaconError(m_researcher->BeaconError()); + } else if (!beacon && !pending_beacon) { + beacon_status = BeaconStatus::NO_BEACON; } else { beacon_status = BeaconStatus::ERROR_MISSING_KEY; } - // If automatic advertisement/renewal encountered a problem, raise this - // error first: - // if (beacon_status != BeaconStatus::ACTIVE) { commitBeacon(beacon_status, beacon, pending_beacon); } else if (pending_beacon) { @@ -583,8 +582,6 @@ void ResearcherModel::updateBeacon() } else { commitBeacon(BeaconStatus::ACTIVE, beacon, pending_beacon); } - } else { - commitBeacon(BeaconStatus::NO_BEACON, beacon, pending_beacon); } } From 8ece5ddbafe2172b05bcdb347b6db8e46b5fca6e Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 00:47:12 +0300 Subject: [PATCH 056/280] Update configure to support C++17 --- build-aux/m4/ax_cxx_compile_stdcxx.m4 | 455 ++++++++++++++++++++++++-- configure.ac | 5 +- 2 files changed, 422 insertions(+), 38 deletions(-) diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 index f147cee3b1..43087b2e68 100644 --- a/build-aux/m4/ax_cxx_compile_stdcxx.m4 +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS @@ -33,21 +33,23 @@ # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 4 +#serial 11 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl - m4_if([$1], [11], [], - [$1], [14], [], - [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], @@ -57,26 +59,13 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) - m4_if([$4], [], [ax_cxx_compile_cxx$1_try_default=true], - [$4], [default], [ax_cxx_compile_cxx$1_try_default=true], - [$4], [nodefault], [ax_cxx_compile_cxx$1_try_default=false], - [m4_fatal([invalid fourth argument `$4' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no - m4_if([$4], [nodefault], [], [dnl - AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, - ax_cv_cxx_compile_cxx$1, - [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], - [ax_cv_cxx_compile_cxx$1=yes], - [ax_cv_cxx_compile_cxx$1=no])]) - if test x$ax_cv_cxx_compile_cxx$1 = xyes; then - ac_success=yes - fi]) - m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then - for switch in -std=gnu++$1 -std=gnu++0x; do + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, @@ -102,22 +91,27 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" - for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, - $cachevar, - [ac_save_CXX="$CXX" - CXX="$CXX $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXX="$ac_save_CXX"]) - if eval test x\$$cachevar = xyes; then - CXX="$CXX $switch" - if test -n "$CXXCPP" ; then - CXXCPP="$CXXCPP $switch" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break fi - ac_success=yes + done + if test x$ac_success = xyes; then break fi done @@ -154,6 +148,11 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) dnl Tests for new features in C++11 @@ -191,11 +190,13 @@ namespace cxx11 struct Base { + virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { + virtual ~Derived() override {} virtual void f() override {} }; @@ -524,7 +525,7 @@ namespace cxx14 } - namespace test_digit_seperators + namespace test_digit_separators { constexpr auto ten_million = 100'000'000; @@ -566,3 +567,385 @@ namespace cxx14 #endif // __cplusplus >= 201402L ]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L + +]]) diff --git a/configure.ac b/configure.ac index 8544370865..6c25599089 100755 --- a/configure.ac +++ b/configure.ac @@ -59,8 +59,9 @@ case $host in lt_cv_deplibs_check_method="pass_all" ;; esac -dnl Require C++11 compiler (no GNU extensions) -AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault]) + +dnl Require C++17 compiler (no GNU extensions) +AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory], [nodefault]) dnl Check if -latomic is required for CHECK_ATOMIC From 9d94f72ebd77f2741e54056659168beb607c2050 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 00:59:18 +0300 Subject: [PATCH 057/280] Use std::clamp --- src/gridcoin/gridcoin.cpp | 4 ++-- src/gridcoin/quorum.cpp | 2 +- src/gridcoin/staking/difficulty.cpp | 6 +++--- src/gridcoin/staking/reward.cpp | 2 +- src/miner.cpp | 2 +- src/test/gridcoin/superblock_tests.cpp | 2 +- src/util.h | 14 -------------- 7 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index e4771aea98..7d519b45ae 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -270,9 +270,9 @@ void ThreadScraperSubscriber(void* parg) void InitializeScraper(ThreadHandlerPtr threads) { // Default to 300 sec (5 min), clamp to 60 minimum, 600 maximum - converted to milliseconds. - nScraperSleep = clamp(GetArg("-scrapersleep", 300), 60, 600) * 1000; + nScraperSleep = std::clamp(GetArg("-scrapersleep", 300), 60, 600) * 1000; // Default to 14400 sec (4 hrs), clamp to 300 minimum, 86400 maximum (meaning active all of the time). - nActiveBeforeSB = clamp(GetArg("-activebeforesb", 14400), 300, 86400); + nActiveBeforeSB = std::clamp(GetArg("-activebeforesb", 14400), 300, 86400); // Run the scraper or subscriber housekeeping thread, but not both. The // subscriber housekeeping thread checks if the flag for the scraper thread diff --git a/src/gridcoin/quorum.cpp b/src/gridcoin/quorum.cpp index 94b2be15af..5ce12ff635 100644 --- a/src/gridcoin/quorum.cpp +++ b/src/gridcoin/quorum.cpp @@ -574,7 +574,7 @@ class SuperblockValidator SuperblockValidator(const SuperblockPtr& superblock, size_t hint_bits = 32) : m_superblock(superblock) , m_quorum_hash(superblock->GetHash()) - , m_hint_shift(32 + clamp(32 - hint_bits, 0, 32)) + , m_hint_shift(32 + std::clamp(32 - hint_bits, 0, 32)) { } diff --git a/src/gridcoin/staking/difficulty.cpp b/src/gridcoin/staking/difficulty.cpp index dd72815af5..7a48935892 100644 --- a/src/gridcoin/staking/difficulty.cpp +++ b/src/gridcoin/staking/difficulty.cpp @@ -206,9 +206,9 @@ double GRC::GetSmoothedDifficulty(int64_t nStakeableBalance) // familiar with the thumbrule for ETTS, ETTS = 10000 / Balance * Diff should recognize it in the below // expression. Note that the actual constant is 9942.2056 (from the bluepaper, eq. 12), but it suffices to // use the rounded thumbrule value here. - unsigned int nEstAppropriateDiffSpan = clamp(10000.0 * BLOCKS_PER_DAY * COIN - / nStakeableBalance * dDiff, - 40, 960); + unsigned int nEstAppropriateDiffSpan = std::clamp(10000.0 * BLOCKS_PER_DAY * COIN + / nStakeableBalance * dDiff, + 40, 960); LogPrint(BCLog::LogFlags::NOISY, "GetSmoothedDifficulty debug: nStakeableBalance: %u", nStakeableBalance); LogPrint(BCLog::LogFlags::NOISY, "GetSmoothedDifficulty debug: nEstAppropriateDiffSpan: %u", nEstAppropriateDiffSpan); diff --git a/src/gridcoin/staking/reward.cpp b/src/gridcoin/staking/reward.cpp index f07b42b022..45d0e08712 100644 --- a/src/gridcoin/staking/reward.cpp +++ b/src/gridcoin/staking/reward.cpp @@ -49,7 +49,7 @@ CAmount GRC::GetConstantBlockReward(const CBlockIndex* index) reward = atoi64(oCBReward.value); } - reward = clamp(reward, MIN_CBR, MAX_CBR); + reward = std::clamp(reward, MIN_CBR, MAX_CBR); return reward; } diff --git a/src/miner.cpp b/src/miner.cpp index b528dc529d..b01fba0f43 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -268,7 +268,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = clamp(nBlockMaxSize, 1000, MAX_BLOCK_SIZE - 1000); + nBlockMaxSize = std::clamp(nBlockMaxSize, 1000, MAX_BLOCK_SIZE - 1000); // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay diff --git a/src/test/gridcoin/superblock_tests.cpp b/src/test/gridcoin/superblock_tests.cpp index b56b2e8419..9aeb755f3f 100644 --- a/src/test/gridcoin/superblock_tests.cpp +++ b/src/test/gridcoin/superblock_tests.cpp @@ -112,7 +112,7 @@ struct Legacy // Ensure we do not blow out the binary space (technically we can handle 0-65535) double magnitude_d = strtod(ExtractValue(entry, ",", 1).c_str(), NULL); // Changed to 65535 for the new NN. This will still be able to be successfully unpacked by any node. - magnitude_d = clamp(magnitude_d, 0.0, 65535.0); + magnitude_d = std::clamp(magnitude_d, 0.0, 65535.0); researcher.magnitude = htobe16(roundint(magnitude_d)); stream.write((const char*) &researcher, sizeof(BinaryResearcher)); diff --git a/src/util.h b/src/util.h index c70fe621bc..73dc711914 100644 --- a/src/util.h +++ b/src/util.h @@ -244,20 +244,6 @@ inline std::string leftTrim(std::string src, char chr) return src; } -// This is effectively straight out of C++ 17 . When we move to C++ 17 we -// can get rid of this and use std::clamp. -template -constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) -{ - return assert(!comp(hi, lo)), comp(v, lo) ? lo : comp(hi, v) ? hi : v; -} - -template -constexpr const T& clamp(const T& v, const T& lo, const T& hi) -{ - return clamp(v, lo, hi, std::less()); -} - inline int64_t GetPerformanceCounter() { int64_t nCounter = 0; From 5657ab02de32b892a952a5e3b84fe9c7c258f613 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 01:26:34 +0300 Subject: [PATCH 058/280] Add compile time verification of assumptions --- src/Makefile.am | 1 + src/compat/assumptions.h | 62 ++++++++++++++++++++++++++++++++++++++++ src/util.h | 1 + 3 files changed, 64 insertions(+) create mode 100644 src/compat/assumptions.h diff --git a/src/Makefile.am b/src/Makefile.am index 698efa48df..287327894a 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,7 @@ GRIDCOIN_CORE_H = \ chainparamsbase.h \ checkpoints.h \ compat.h \ + compat/assumptions.h \ compat/byteswap.h \ compat/endian.h \ consensus/consensus.h \ diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h new file mode 100644 index 0000000000..5f50cde3ff --- /dev/null +++ b/src/compat/assumptions.h @@ -0,0 +1,62 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Compile-time verification of assumptions we make. + +#ifndef BITCOIN_COMPAT_ASSUMPTIONS_H +#define BITCOIN_COMPAT_ASSUMPTIONS_H + +#include + +// Assumption: We assume that the macro NDEBUG is not defined. +// Example(s): We use assert(...) extensively with the assumption of it never +// being a noop at runtime. +#if defined(NDEBUG) +# error "Bitcoin cannot be compiled without assertions." +#endif + +// Assumption: We assume a C++17 (ISO/IEC 14882:2017) compiler (minimum requirement). +// Example(s): We assume the presence of C++17 features everywhere :-) +// ISO Standard C++17 [cpp.predefined]p1: +// "The name __cplusplus is defined to the value 201703L when compiling a C++ +// translation unit." +static_assert(__cplusplus >= 201703L, "C++17 standard assumed"); + +// Assumption: We assume the floating-point types to fulfill the requirements of +// IEC 559 (IEEE 754) standard. +// Example(s): Floating-point division by zero in ConnectBlock, CreateTransaction +// and EstimateMedianVal. +static_assert(std::numeric_limits::is_iec559, "IEEE 754 float assumed"); +static_assert(std::numeric_limits::is_iec559, "IEEE 754 double assumed"); + +// Assumption: We assume eight bits per byte (obviously, but remember: don't +// trust -- verify!). +// Example(s): Everywhere :-) +static_assert(std::numeric_limits::digits == 8, "8-bit byte assumed"); + +// Assumption: We assume floating-point widths. +// Example(s): Type punning in serialization code (ser_{float,double}_to_uint{32,64}). +static_assert(sizeof(float) == 4, "32-bit float assumed"); +static_assert(sizeof(double) == 8, "64-bit double assumed"); + +// Assumption: We assume integer widths. +// Example(s): GetSizeOfCompactSize and WriteCompactSize in the serialization +// code. +static_assert(sizeof(short) == 2, "16-bit short assumed"); +static_assert(sizeof(int) == 4, "32-bit int assumed"); +static_assert(sizeof(unsigned) == 4, "32-bit unsigned assumed"); + +// Assumption: We assume size_t to be 32-bit or 64-bit. +// Example(s): size_t assumed to be at least 32-bit in ecdsa_signature_parse_der_lax(...). +// size_t assumed to be 32-bit or 64-bit in MallocUsage(...). +static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t assumed to be 32-bit or 64-bit"); +static_assert(sizeof(size_t) == sizeof(void*), "Sizes of size_t and void* assumed to be equal"); + +// Some important things we are NOT assuming (non-exhaustive list): +// * We are NOT assuming a specific value for std::endian::native. +// * We are NOT assuming a specific value for std::locale("").name(). +// * We are NOT assuming a specific value for std::numeric_limits::is_signed. + +#endif // BITCOIN_COMPAT_ASSUMPTIONS_H diff --git a/src/util.h b/src/util.h index 73dc711914..bd932e7eb3 100644 --- a/src/util.h +++ b/src/util.h @@ -27,6 +27,7 @@ #include #include +#include "compat/assumptions.h" // After merging some more of Bitcoin's utilities, we can split them out // of this file to reduce the header load: From 205f9446de23534a293e01b5f829c2bd81aa7641 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 01:27:08 +0300 Subject: [PATCH 059/280] Use std::size instead of ARRAYLEN --- src/net.cpp | 14 +++++++------- src/protocol.cpp | 6 +++--- src/rpc/client.cpp | 9 +++------ src/rpc/server.cpp | 8 ++------ src/test/base32_tests.cpp | 2 +- src/test/base64_tests.cpp | 2 +- src/util/strencodings.h | 2 -- src/wallet/rpcdump.cpp | 6 ++---- 8 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 4e7f95033d..d3df270f51 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1307,13 +1307,13 @@ void ThreadDNSAddressSeed2(void* parg) { LogPrint(BCLog::LogFlags::NET, "Loading addresses from DNS seeds (could take a while)"); - for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { + for (const auto& seed : strDNSSeed) { if (HaveNameProxy()) { - AddOneShot(strDNSSeed[seed_idx][1]); + AddOneShot(seed[1]); } else { vector vaddr; vector vAdd; - if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) + if (LookupHost(seed[1], vaddr)) { for (auto const& ip : vaddr) { @@ -1324,7 +1324,7 @@ void ThreadDNSAddressSeed2(void* parg) found++; } } - addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true)); + addrman.Add(vAdd, CNetAddr(seed[0], true)); } } } @@ -1526,7 +1526,7 @@ void ThreadOpenConnections2(void* parg) if (addrman.size()==0 && (GetAdjustedTime() - nStart > 60) && !fTestNet) { std::vector vAdd; - for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++) + for (const auto& seed : pnSeed) { // It'll only connect to one or two seed nodes because once it connects, // it'll get a pile of addresses with newer timestamps. @@ -1534,9 +1534,9 @@ void ThreadOpenConnections2(void* parg) // weeks ago. const int64_t nOneWeek = 7*24*60*60; struct in_addr ip; - memcpy(&ip, &pnSeed[i], sizeof(ip)); + memcpy(&ip, &seed, sizeof(ip)); CAddress addr(CService(ip, GetDefaultPort())); - addr.nTime = GetAdjustedTime()-GetRand(nOneWeek)-nOneWeek; + addr.nTime = GetAdjustedTime() - GetRand(nOneWeek) - nOneWeek; vAdd.push_back(addr); } addrman.Add(vAdd, CNetAddr("127.0.0.1")); diff --git a/src/protocol.cpp b/src/protocol.cpp index 87bc4495d0..7db42fc240 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -91,7 +91,7 @@ CInv::CInv(int typeIn, const uint256& hashIn) : type(typeIn), hash(hashIn) {} CInv::CInv(const std::string& strType, const uint256& hashIn) { unsigned int i; - for (i = 1; i < ARRAYLEN(ppszTypeName); i++) + for (i = 1; i < std::size(ppszTypeName); i++) { if (strType == ppszTypeName[i]) { @@ -99,7 +99,7 @@ CInv::CInv(const std::string& strType, const uint256& hashIn) break; } } - if (i == ARRAYLEN(ppszTypeName)) + if (i == std::size(ppszTypeName)) throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType)); hash = hashIn; } @@ -111,7 +111,7 @@ bool operator<(const CInv& a, const CInv& b) bool CInv::IsKnownType() const { - return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName)); + return (type >= 1 && type < (int)std::size(ppszTypeName)); } const char* CInv::GetCommand() const diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index b7195b3d22..dc0f6dd21a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -247,12 +247,9 @@ class CRPCConvertTable CRPCConvertTable::CRPCConvertTable() { - const unsigned int n_elem = - (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); - - for (unsigned int i = 0; i < n_elem; i++) { - members.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramIdx)); + for (const auto& elem : vRPCConvertParams) { + members.insert(std::make_pair(elem.methodName, + elem.paramIdx)); } } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index ef075b57e1..95bd223126 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -461,13 +461,9 @@ static constexpr const char* DEPRECATED_RPCS[] { CRPCTable::CRPCTable() { - unsigned int vcidx; - for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + for (const auto& cmd : vRPCCommands) { - const CRPCCommand *pcmd; - - pcmd = &vRPCCommands[vcidx]; - mapCommands[pcmd->name] = pcmd; + mapCommands[cmd.name] = &cmd; } } diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp index fdf3285913..2766103f12 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -8,7 +8,7 @@ BOOST_AUTO_TEST_CASE(base32_testvectors) { static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; static const std::string vstrOut[] = {"","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"}; - for (unsigned int i=0; i #include -#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) - /** Used by SanitizeString() */ enum SafeChars { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 2852f79296..66711a26dc 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -29,8 +29,6 @@ const std::locale formats[] = { std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d")) }; -const size_t formats_n = sizeof(formats)/sizeof(formats[0]); - std::time_t pt_to_time_t(const bt::ptime& pt) { bt::ptime timet_start(boost::gregorian::date(1970,1,1)); @@ -42,10 +40,10 @@ int64_t DecodeDumpTime(const std::string& s) { bt::ptime pt; - for(size_t i=0; i> pt; if(pt != bt::ptime()) break; } From 8f26ffb2528baccd6b9e07a2332707aff088f2a1 Mon Sep 17 00:00:00 2001 From: fanquake Date: Tue, 24 Nov 2020 07:29:11 +0800 Subject: [PATCH 060/280] depends: build qt in c++17 mode --- depends/packages/qt.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 625ca60757..39f501bf5d 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -36,7 +36,7 @@ define $(package)_set_vars $(package)_config_opts_release = -release $(package)_config_opts_debug = -debug $(package)_config_opts += -bindir $(build_prefix)/bin -$(package)_config_opts += -c++std c++11 +$(package)_config_opts += -c++std c++1z $(package)_config_opts += -confirm-license $(package)_config_opts += -hostprefix $(build_prefix) $(package)_config_opts += -no-compile-examples From ce9f7ee8ec30d5523ff356fdd1eaf4ca2bd3afba Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 02:16:18 +0300 Subject: [PATCH 061/280] depends: build bdb with -std=c++17 --- depends/packages/bdb.mk | 2 +- depends/packages/bdb53.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 05da9acf6a..8c7f825571 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -11,7 +11,7 @@ define $(package)_set_vars $(package)_config_opts+=--libdir=$($($(package)_type)_prefix)/lib $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic - $(package)_cxxflags=-std=c++11 + $(package)_cxxflags=-std=c++17 $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) $(package)_cflags_aarch64_linux = $(GCCFLAGS) $(package)_cxxflags_arm_linux = $(GCCFLAGS) diff --git a/depends/packages/bdb53.mk b/depends/packages/bdb53.mk index 2a9af2010d..1229c70f9c 100644 --- a/depends/packages/bdb53.mk +++ b/depends/packages/bdb53.mk @@ -11,7 +11,7 @@ define $(package)_set_vars $(package)_config_opts+=--libdir=$($($(package)_type)_prefix)/lib $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic - $(package)_cxxflags=-std=c++11 + $(package)_cxxflags=-std=c++17 $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) $(package)_cflags_aarch64_linux = $(GCCFLAGS) $(package)_cxxflags_arm_linux = $(GCCFLAGS) From ce15088437039bc39fcae09a117dc6e3031739b9 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 02:16:54 +0300 Subject: [PATCH 062/280] depends: build Boost with -std=c++17 --- depends/packages/boost.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 6c8922fda0..118981c1f3 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -28,7 +28,7 @@ ifneq (,$(findstring clang,$($(package)_cxx))) endif $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,iostreams -$(package)_cxxflags=-std=c++11 -fvisibility=hidden +$(package)_cxxflags=-std=c++17 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_android=-fPIC endef From 32b963711e6b6de5e67c78c45807ed5f67495ead Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 02:22:23 +0300 Subject: [PATCH 063/280] Use [[nodiscard]] instead of NODISCARD --- src/attributes.h | 17 +++++++---------- src/util/strencodings.cpp | 2 +- src/util/strencodings.h | 12 ++++++------ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/attributes.h b/src/attributes.h index 45099bd8b8..9957bcd84b 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -1,22 +1,19 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_ATTRIBUTES_H #define BITCOIN_ATTRIBUTES_H -#if defined(__has_cpp_attribute) -# if __has_cpp_attribute(nodiscard) -# define NODISCARD [[nodiscard]] -# endif -#endif -#ifndef NODISCARD -# if defined(_MSC_VER) && _MSC_VER >= 1700 -# define NODISCARD _Check_return_ +#if defined(__clang__) +# if __has_attribute(lifetimebound) +# define LIFETIMEBOUND [[clang::lifetimebound]] # else -# define NODISCARD __attribute__((warn_unused_result)) +# define LIFETIMEBOUND # endif +#else +# define LIFETIMEBOUND #endif #endif // BITCOIN_ATTRIBUTES_H diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 1e7d24c71c..4c2f713cc9 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -263,7 +263,7 @@ std::string DecodeBase32(const std::string& str, bool* pf_invalid) return std::string((const char*)vchRet.data(), vchRet.size()); } -NODISCARD static bool ParsePrechecks(const std::string& str) +[[nodiscard]] static bool ParsePrechecks(const std::string& str) { if (str.empty()) // No empty string allowed return false; diff --git a/src/util/strencodings.h b/src/util/strencodings.h index bafb1d4351..9b9ac6fa58 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -89,35 +89,35 @@ constexpr inline bool IsSpace(char c) noexcept { * @returns true if the entire string could be parsed as valid integer, * false if not the entire string could be parsed or when overflow or underflow occurred. */ -NODISCARD bool ParseInt32(const std::string& str, int32_t *out); +[[nodiscard]] bool ParseInt32(const std::string& str, int32_t *out); /** * Convert string to signed 64-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, * false if not the entire string could be parsed or when overflow or underflow occurred. */ -NODISCARD bool ParseInt64(const std::string& str, int64_t *out); +[[nodiscard]] bool ParseInt64(const std::string& str, int64_t *out); /** * Convert decimal string to unsigned 32-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, * false if not the entire string could be parsed or when overflow or underflow occurred. */ -NODISCARD bool ParseUInt32(const std::string& str, uint32_t *out); +[[nodiscard]] bool ParseUInt32(const std::string& str, uint32_t *out); /** * Convert decimal string to unsigned 64-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, * false if not the entire string could be parsed or when overflow or underflow occurred. */ -NODISCARD bool ParseUInt64(const std::string& str, uint64_t *out); +[[nodiscard]] bool ParseUInt64(const std::string& str, uint64_t *out); /** * Convert string to double with strict parse error feedback. * @returns true if the entire string could be parsed as valid double, * false if not the entire string could be parsed or when overflow or underflow occurred. */ -NODISCARD bool ParseDouble(const std::string& str, double *out); +[[nodiscard]] bool ParseDouble(const std::string& str, double *out); template std::string HexStr(const T itbegin, const T itend) @@ -167,7 +167,7 @@ bool TimingResistantEqual(const T& a, const T& b) * @returns true on success, false on error. * @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger. */ -NODISCARD bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +[[nodiscard]] bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); /** Convert from one power-of-2 number base to another. */ template From ff9bafdf39040f4f2cbb79207128173db2b1a194 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 02:40:16 +0300 Subject: [PATCH 064/280] scripted-diff: remove MakeUnique() -BEGIN VERIFY SCRIPT- git rm src/util/memory.h sed -i -e 's/MakeUnique/std::make_unique/g' $(git grep -l MakeUnique src) sed -i -e '/util\/memory.h \\/d' src/Makefile.am sed -i -e '/#include [<"]util\/memory.h[">]/d' $(git grep -l 'util/memory.h' src) -END VERIFY SCRIPT- --- src/Makefile.am | 1 - src/chainparamsbase.cpp | 4 ++-- src/chainparamsbase.h | 1 - src/gridcoin/tally.cpp | 8 ++++---- src/gridcoin/voting/builders.cpp | 4 ++-- src/init.cpp | 2 +- src/test/allocator_tests.cpp | 3 +-- src/test/test_gridcoin.cpp | 2 +- src/util.cpp | 3 +-- src/util/memory.h | 19 ------------------- 10 files changed, 12 insertions(+), 35 deletions(-) delete mode 100644 src/util/memory.h diff --git a/src/Makefile.am b/src/Makefile.am index 287327894a..90a9e9fcee 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -169,7 +169,6 @@ GRIDCOIN_CORE_H = \ txdb-leveldb.h \ ui_interface.h \ uint256.h \ - util/memory.h \ util/reverse_iterator.h \ util/strencodings.h \ util/threadnames.h \ diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 83ba2ae87e..962d50d4c7 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -23,9 +23,9 @@ const CBaseChainParams& BaseParams() std::unique_ptr CreateBaseChainParams(const std::string& chain) { if (chain == CBaseChainParams::MAIN) - return MakeUnique("", 32749); + return std::make_unique("", 32749); else if (chain == CBaseChainParams::TESTNET) - return MakeUnique("testnet", 32748); + return std::make_unique("testnet", 32748); else throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 1e9b9df4bb..d0cd6de30b 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -4,7 +4,6 @@ #pragma once -#include "util/memory.h" #include #include diff --git a/src/gridcoin/tally.cpp b/src/gridcoin/tally.cpp index 0def673741..985c497b34 100644 --- a/src/gridcoin/tally.cpp +++ b/src/gridcoin/tally.cpp @@ -1119,7 +1119,7 @@ AccrualComputer Tally::GetComputer( const CBlockIndex* const last_block_ptr) { if (!last_block_ptr) { - return MakeUnique(); + return std::make_unique(); } if (last_block_ptr->nVersion >= 11) { @@ -1136,7 +1136,7 @@ AccrualComputer Tally::GetSnapshotComputer( const CBlockIndex* const last_block_ptr, const SuperblockPtr superblock) { - return MakeUnique( + return std::make_unique( cpid, account, payment_time, @@ -1165,7 +1165,7 @@ AccrualComputer Tally::GetLegacyComputer( const ResearchAccount& account = GetAccount(cpid); if (!account.IsActive(last_block_ptr->nHeight)) { - return MakeUnique( + return std::make_unique( cpid, account, payment_time, @@ -1173,7 +1173,7 @@ AccrualComputer Tally::GetLegacyComputer( Quorum::CurrentSuperblock()->m_cpids.MagnitudeOf(cpid).Floating()); } - return MakeUnique( + return std::make_unique( cpid, account, Quorum::CurrentSuperblock()->m_cpids.MagnitudeOf(cpid).Floating(), diff --git a/src/gridcoin/voting/builders.cpp b/src/gridcoin/voting/builders.cpp index 5e9bd66572..a539cf0a8f 100644 --- a/src/gridcoin/voting/builders.cpp +++ b/src/gridcoin/voting/builders.cpp @@ -872,7 +872,7 @@ void GRC::SendVoteContract(VoteBuilder builder) // Class: PollBuilder // ----------------------------------------------------------------------------- -PollBuilder::PollBuilder() : m_poll(MakeUnique()) +PollBuilder::PollBuilder() : m_poll(std::make_unique()) { } @@ -1115,7 +1115,7 @@ CWalletTx PollBuilder::BuildContractTx(CWallet* const pwallet) VoteBuilder::VoteBuilder(const Poll& poll, const uint256 poll_txid) : m_poll(&poll) - , m_vote(MakeUnique()) + , m_vote(std::make_unique()) { m_vote->m_poll_txid = poll_txid; } diff --git a/src/init.cpp b/src/init.cpp index de68e694df..2fae42b68b 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -1080,7 +1080,7 @@ bool AppInit2(ThreadHandlerPtr threads) // Ban manager instance should not already be instantiated assert(!g_banman); // Create ban manager instance. - g_banman = MakeUnique(GetDataDir() / "banlist.dat", &uiInterface, GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); + g_banman = std::make_unique(GetDataDir() / "banlist.dat", &uiInterface, GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); uiInterface.InitMessage(_("Loading addresses...")); LogPrint(BCLog::LogFlags::NOISY, "Loading addresses..."); diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index 0020f121c3..834dd323f4 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -2,7 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include //#include //#include @@ -165,7 +164,7 @@ class TestLockedPageAllocator: public LockedPageAllocator BOOST_AUTO_TEST_CASE(lockedpool_tests_mock) { // Test over three virtual arenas, of which one will succeed being locked - std::unique_ptr x = MakeUnique(3, 1); + std::unique_ptr x = std::make_unique(3, 1); LockedPool pool(std::move(x)); BOOST_CHECK(pool.stats().total == 0); BOOST_CHECK(pool.stats().locked == 0); diff --git a/src/test/test_gridcoin.cpp b/src/test/test_gridcoin.cpp index f91b95882d..d610fb0c81 100644 --- a/src/test/test_gridcoin.cpp +++ b/src/test/test_gridcoin.cpp @@ -39,7 +39,7 @@ struct TestingSetup { // Ban manager instance should not already be instantiated assert(!g_banman); // Create ban manager instance. - g_banman = MakeUnique(GetDataDir() / "banlist.dat", &uiInterface, GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); + g_banman = std::make_unique(GetDataDir() / "banlist.dat", &uiInterface, GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); } ~TestingSetup() { diff --git a/src/util.cpp b/src/util.cpp index f2c15c58d2..9f095d2c60 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -9,7 +9,6 @@ #include "version.h" #include "ui_interface.h" #include "util.h" -#include "util/memory.h" #include // for to_lower() #include @@ -836,7 +835,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b // Create empty lock file if it doesn't exist. FILE* file = fsbridge::fopen(pathLockFile, "a"); if (file) fclose(file); - auto lock = MakeUnique(pathLockFile); + auto lock = std::make_unique(pathLockFile); if (!lock->TryLock()) { return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason()); } diff --git a/src/util/memory.h b/src/util/memory.h deleted file mode 100644 index 15ecf8f80d..0000000000 --- a/src/util/memory.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2018 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_UTIL_MEMORY_H -#define BITCOIN_UTIL_MEMORY_H - -#include -#include - -//! Substitute for C++14 std::make_unique. -template -std::unique_ptr MakeUnique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -#endif From 5879e10f500a6d3d49e28910de4a183a01f2b0d1 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Mon, 12 Apr 2021 10:27:36 -0400 Subject: [PATCH 065/280] Remove deprecated RPC commands --- src/rpc/blockchain.cpp | 10 --- src/rpc/dataacq.cpp | 175 ----------------------------------------- src/rpc/server.cpp | 5 -- src/rpc/server.h | 5 -- 4 files changed, 195 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index efbefa5384..46d61871cf 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1890,11 +1890,6 @@ UniValue networktime(const UniValue& params, bool fHelp) return res; } -UniValue execute(const UniValue& params, bool fHelp) -{ - throw JSONRPCError(RPC_DEPRECATED, "execute function has been deprecated; run the command as previously done so but without execute"); -} - UniValue SuperblockReport(int lookback, bool displaycontract, std::string cpid) { UniValue results(UniValue::VARR); @@ -2037,11 +2032,6 @@ UniValue GetJSONVersionReport(const int64_t lookback, const bool full_version) return json; } -UniValue listitem(const UniValue& params, bool fHelp) -{ - throw JSONRPCError(RPC_DEPRECATED, "list is deprecated; Please run the command the same as previously without list"); -} - // ppcoin: get information of sync-checkpoint UniValue getcheckpoint(const UniValue& params, bool fHelp) { diff --git a/src/rpc/dataacq.cpp b/src/rpc/dataacq.cpp index f3e983109e..dadbde6dee 100644 --- a/src/rpc/dataacq.cpp +++ b/src/rpc/dataacq.cpp @@ -329,181 +329,6 @@ UniValue rpc_getblockstats(const UniValue& params, bool fHelp) return result1; } -UniValue rpc_getsupervotes(const UniValue& params, bool fHelp) -{ - if(fHelp || params.size() != 2 ) - throw runtime_error( - "getsupervotes mode superblock\n" - "Report votes for specified superblock.\n" - "mode: 0=text, 1,2=json\n" - "superblock: block hash or last= currently active, now= ongoing sb votes.\n" - ); - long mode= RoundFromString(params[0].get_str(),0); - CBlockIndex* pStart=NULL; - long nMaxDepth_weight= 0; - UniValue result1(UniValue::VOBJ); - if("last"==params[1].get_str()) - { - const uint64_t height = Quorum::CurrentSuperblock().m_height; - if(!height) - { - result1.pushKV("error","No superblock loaded"); - return result1; - } - CBlockIndex* pblockindex = RPCBlockFinder.FindByHeight(height); - if(!pblockindex) - { - result1.pushKV("height_cache", height); - result1.pushKV("error","Superblock not found in block index"); - return result1; - } - if(!pblockindex->IsSuperblock()) - { - result1.pushKV("height_cache", height); - result1.pushKV("block_hash",pblockindex->GetBlockHash().GetHex()); - result1.pushKV("error","Superblock loaded not a Superblock"); - return result1; - } - pStart=pblockindex; - - /* sb votes are evaluated on content of the previous block */ - nMaxDepth_weight= pStart->nHeight -1; - } - else - if("now"==params[1].get_str()) - { - LOCK(cs_main); - pStart=pindexBest; - nMaxDepth_weight= pStart->nHeight; - } - else - { - LOCK(cs_main); - uint256 hash = uint256S(params[1].get_str()); - - if (mapBlockIndex.count(hash) == 0) - { - result1.pushKV("error","Block hash not found in block index"); - return result1; - } - - - CBlockIndex* pblockindex = mapBlockIndex[hash]; - - if(!pblockindex->IsSuperblock()) - { - result1.pushKV("block_hash",pblockindex->GetBlockHash().GetHex()); - result1.pushKV("error","Requested block is not a Superblock"); - return result1; - } - pStart = pblockindex; - - /* sb votes are evaluated on content of the previous block */ - nMaxDepth_weight= pStart->nHeight -1; - } - - { - UniValue info(UniValue::VOBJ); - CBlock block; - if(!block.ReadFromDisk(pStart->nFile,pStart->nBlockPos,true)) - throw runtime_error("failed to read block"); - //assert(block.vtx.size() > 0); - const Claim claim = block.GetClaim(); - const Superblock& sb = *claim.m_superblock; - - info.pushKV("block_hash",pStart->GetBlockHash().GetHex()); - info.pushKV("height",pStart->nHeight); - info.pushKV("quorum_hash", claim.m_quorum_hash.ToString()); - - if (sb.m_version == 1) { - info.pushKV("packed_size", (int64_t)sb.PackLegacy().size()); - } else { - info.pushKV("packed_size", (int64_t)GetSerializeSize(sb, 1, 1)); - } - - info.pushKV("contract_hash", QuorumHash::Hash(sb).ToString()); - result1.pushKV("info", info ); - } - - UniValue votes(UniValue::VOBJ); - - long blockcount=0; - long maxblocks= 200; - - CBlockIndex* cur = pStart; - - for( ; (cur - &&( blockcountpprev, ++blockcount - ) - { - - double diff = GRC::GetDifficulty(cur); - signed int delta = 0; - if(cur->pprev) - delta = (cur->nTime - cur->pprev->nTime); - - CBlock block; - if(!block.ReadFromDisk(cur->nFile,cur->nBlockPos,true)) - throw runtime_error("failed to read block"); - //assert(block.vtx.size() > 0); - const Claim& claim = block.GetClaim(); - - if(!claim.m_quorum_hash.Valid()) - continue; - - uint64_t stakeout = 0; - if(block.vtx.size()>1 && block.vtx[1].vout.size()>1) - { - stakeout += block.vtx[1].vout[1].nValue; - if(block.vtx[1].vout.size()>2) - stakeout += block.vtx[1].vout[2].nValue; - //could have used for loop - } - - long distance= (nMaxDepth_weight-cur->nHeight)+10; - double multiplier = 200; - if (distance < 40) multiplier = 400; - double weight = (1.0/distance)*multiplier; - - if(mode==0) - { - std::string line - = claim.m_quorum_hash.ToString() - + "|"+RoundToString(weight/10.0,5) - + "|"+claim.m_organization - + "|"+claim.m_client_version - + "|"+RoundToString(diff,3) - + "|"+RoundToString(delta,0) - + "|"+claim.m_mining_id.ToString() - ; - votes.pushKV(ToString(cur->nHeight), line ); - } - else - { - UniValue result2(UniValue::VOBJ); - result2.pushKV("quorum_hash", claim.m_quorum_hash.ToString()); - result2.pushKV("weight", weight ); - result2.pushKV("cpid", cur->GetMiningId().ToString() ); - result2.pushKV("organization", claim.m_organization); - result2.pushKV("cversion", claim.m_client_version); - if(mode>=2) - { - result2.pushKV("difficulty", diff ); - result2.pushKV("delay", delta ); - result2.pushKV("hash", cur->GetBlockHash().GetHex() ); - result2.pushKV("stakeout", (double) stakeout / COIN ); - } - votes.pushKV(ToString(cur->nHeight), result2 ); - } - - } - result1.pushKV("votes", votes ); - - return result1; -} - UniValue rpc_exportstats(const UniValue& params, bool fHelp) { if(fHelp) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index ef075b57e1..2327b5d153 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -281,9 +281,7 @@ UniValue stop(const UniValue& params, bool fHelp) static const CRPCCommand vRPCCommands[] = { // name function category // ------------------------ ----------------------- ----------------- - { "list", &listitem, cat_null }, { "help", &help, cat_null }, - { "execute", &execute, cat_null }, // Wallet commands { "addmultisigaddress", &addmultisigaddress, cat_wallet }, @@ -375,7 +373,6 @@ static const CRPCCommand vRPCCommands[] = { "getblockstats", &rpc_getblockstats, cat_developer }, { "getlistof", &getlistof, cat_developer }, { "getrecentblocks", &rpc_getrecentblocks, cat_developer }, - { "getsupervotes", &rpc_getsupervotes, cat_developer }, { "inspectaccrualsnapshot", &inspectaccrualsnapshot, cat_developer }, { "listdata", &listdata, cat_developer }, { "listprojects", &listprojects, cat_developer }, @@ -446,14 +443,12 @@ static const CRPCCommand vRPCCommands[] = static constexpr const char* DEPRECATED_RPCS[] { "debug", - "execute" , "getaccount", "getaccountaddress", "getaddressesbyaccount", "getreceivedbyaccount", "listaccounts", "listreceivedbyaccount", - "list", "move", "setaccount", "vote", diff --git a/src/rpc/server.h b/src/rpc/server.h index 46061eec4a..c985a992d5 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -99,10 +99,6 @@ extern std::vector ParseHexV(const UniValue& v, std::string strNa // Rpc reordered by category -// Deprecated -extern UniValue listitem(const UniValue& params, bool fHelp); -extern UniValue execute(const UniValue& params, bool fHelp); - // Wallet extern UniValue addmultisigaddress(const UniValue& params, bool fHelp); extern UniValue addredeemscript(const UniValue& params, bool fHelp); @@ -246,7 +242,6 @@ extern UniValue listbanned(const UniValue& params, bool fHelp); extern UniValue memorypool(const UniValue& params, bool fHelp); extern UniValue networktime(const UniValue& params, bool fHelp); extern UniValue ping(const UniValue& params, bool fHelp); -extern UniValue rpc_getsupervotes(const UniValue& params, bool fHelp); extern UniValue rpc_exportstats(const UniValue& params, bool fHelp); extern UniValue rpc_getrecentblocks(const UniValue& params, bool fHelp); extern UniValue setban(const UniValue& params, bool fHelp); From b6ad2ca4c4c05cec133d8c2e08ac8c42a919f305 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Fri, 23 Apr 2021 15:32:23 -0500 Subject: [PATCH 066/280] Fix GUI dark theme link text color visibility This sets the application's palette color for links to the shade of blue from #847. It improves the visibility of links when using the dark mode. --- src/qt/bitcoingui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bb46a4ea26..aa444097a0 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -143,6 +143,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): QPalette pal(qApp->palette()); pal.setColor(QPalette::Base, QColor(0, 0, 0, 0)); pal.setColor(QPalette::Mid, pal.color(QPalette::Base)); + // Set links to a lighter shade of blue for readability in the dark theme: + pal.setColor(QPalette::Link, QColor(73, 144, 226)); + pal.setColor(QPalette::LinkVisited, QColor(73, 144, 226)); qApp->setPalette(pal); setWindowTitle(tr("Gridcoin") + " " + tr("Wallet")); From c15e91809f5425e930a29a1b3d5b10d1bc63d5d0 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Fri, 23 Apr 2021 12:40:41 -0500 Subject: [PATCH 067/280] Set standard base Qt style on Windows and macOS This sets the "Fusion" Qt theme as the application's base style for Windows and macOS to normalize some minor appearance issues between different platforms. Fusion is the default style used for Linux for desktops without a custom window manager theme. From what I read, the Windows theme still has some high-DPI issues, and the Fusion styles support high-DPI displays very well. --- src/qt/bitcoin.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index acd317009b..c870ad3972 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -241,7 +241,7 @@ int main(int argc, char *argv[]) // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#if QT_VERSION >= 0x050600 && !defined(WIN32) +#if QT_VERSION >= 0x050600 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif @@ -264,6 +264,20 @@ int main(int argc, char *argv[]) else app.setApplicationName("Gridcoin-Qt"); +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + // Apply Qt's built-in "Fusion" theme as the application's base styles to + // normalize layout discrepancies between platforms and fix some high-DPI + // scaling issues on Windows. Gridcoin uses highly-customized stylesheets + // which obscure most of the platform's styles anyway. That said, respect + // the presence of Qt's "-style" option to bypass this if necessary. Skip + // the override on Linux for now so that a user's window manager Qt theme + // comes through for widgets without an explicit application style. + // + if (!IsArgSet("-style")) { + app.setStyle("Fusion"); + } +#endif + // Install global event filter that makes sure that long tooltips can be word-wrapped app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); From 7e564d8f4fdf5b5a6f6eccf026a2d948394f0595 Mon Sep 17 00:00:00 2001 From: sweede-se <79360493+sweede-se@users.noreply.github.com> Date: Sat, 24 Apr 2021 10:41:48 +0200 Subject: [PATCH 068/280] wallet: update addnode Nobody asked for it but I did it anyway. Don't know if this will have implication on Testnet if the added nodes don't run Testnet. Added new nodes and removed dead ones where the hostname resolution failed according to https://addnode.cycy.me --- src/init.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index de68e694df..4947d7ef0b 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -190,12 +190,20 @@ static void CreateNewConfigFile() myConfig << "addnode=addnode-us-central.cycy.me\n" + << "addnode=ch.gridcoin.pl\n" << "addnode=ec2-3-81-39-58.compute-1.amazonaws.com\n" - << "addnode=gridcoin.crypto.fans\n" + << "addnode=fi.gridcoin.pl\n" + << "addnode=grcnode.tahvok.com\n" + << "addnode=grcnode.thefoxie.eu\n" + << "addnode=gridcoin.bunnyfeet.fi\n" + << "addnode=gridcoin.network\n" << "addnode=gridcoin.ddns.net\n" - << "addnode=london.grcnode.co.uk\n" + << "addnode=gridhost.ddns.net\n" + << "addnode=node.gridcoin.network\n" << "addnode=seeds.gridcoin.ifoggz-network.xyz\n" << "addnode=seed.gridcoin.pl\n" + << "addnode=swe.tplinkdns.com\n" + << "addnode=tarmoilves.eu\n" << "addnode=www.grcpool.com\n"; } From 31b24a0d8e5c12863d9a8da93b1945fe2c6c0c16 Mon Sep 17 00:00:00 2001 From: sweede-se <79360493+sweede-se@users.noreply.github.com> Date: Sat, 24 Apr 2021 11:08:19 +0200 Subject: [PATCH 069/280] Update init.cpp --- src/init.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 4947d7ef0b..89da02bc9a 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -190,20 +190,20 @@ static void CreateNewConfigFile() myConfig << "addnode=addnode-us-central.cycy.me\n" - << "addnode=ch.gridcoin.pl\n" + << "addnode=ch.gridcoin.pl\n" << "addnode=ec2-3-81-39-58.compute-1.amazonaws.com\n" - << "addnode=fi.gridcoin.pl\n" - << "addnode=grcnode.tahvok.com\n" - << "addnode=grcnode.thefoxie.eu\n" - << "addnode=gridcoin.bunnyfeet.fi\n" - << "addnode=gridcoin.network\n" + << "addnode=fi.gridcoin.pl\n" + << "addnode=grcnode.tahvok.com\n" + << "addnode=grcnode.thefoxie.eu\n" + << "addnode=gridcoin.bunnyfeet.fi\n" + << "addnode=gridcoin.network\n" << "addnode=gridcoin.ddns.net\n" - << "addnode=gridhost.ddns.net\n" - << "addnode=node.gridcoin.network\n" + << "addnode=gridhost.ddns.net\n" + << "addnode=node.gridcoin.network\n" << "addnode=seeds.gridcoin.ifoggz-network.xyz\n" << "addnode=seed.gridcoin.pl\n" - << "addnode=swe.tplinkdns.com\n" - << "addnode=tarmoilves.eu\n" + << "addnode=swe.tplinkdns.com\n" + << "addnode=tarmoilves.eu\n" << "addnode=www.grcpool.com\n"; } From d0522d7a3037c76f1ff503d0752b7123fc185d33 Mon Sep 17 00:00:00 2001 From: sweede-se <79360493+sweede-se@users.noreply.github.com> Date: Sat, 24 Apr 2021 19:59:57 +0200 Subject: [PATCH 070/280] Update init.cpp --- src/init.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 89da02bc9a..c84be421b5 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -190,9 +190,7 @@ static void CreateNewConfigFile() myConfig << "addnode=addnode-us-central.cycy.me\n" - << "addnode=ch.gridcoin.pl\n" << "addnode=ec2-3-81-39-58.compute-1.amazonaws.com\n" - << "addnode=fi.gridcoin.pl\n" << "addnode=grcnode.tahvok.com\n" << "addnode=grcnode.thefoxie.eu\n" << "addnode=gridcoin.bunnyfeet.fi\n" From ce1dd33bdc34f2a8e1f46099b8b3177d0359e847 Mon Sep 17 00:00:00 2001 From: sweede-se <79360493+sweede-se@users.noreply.github.com> Date: Sat, 24 Apr 2021 20:21:08 +0200 Subject: [PATCH 071/280] Update init.cpp --- src/init.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index c84be421b5..8fe2b5e6d2 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -191,17 +191,11 @@ static void CreateNewConfigFile() myConfig << "addnode=addnode-us-central.cycy.me\n" << "addnode=ec2-3-81-39-58.compute-1.amazonaws.com\n" - << "addnode=grcnode.tahvok.com\n" << "addnode=grcnode.thefoxie.eu\n" - << "addnode=gridcoin.bunnyfeet.fi\n" - << "addnode=gridcoin.network\n" << "addnode=gridcoin.ddns.net\n" - << "addnode=gridhost.ddns.net\n" - << "addnode=node.gridcoin.network\n" << "addnode=seeds.gridcoin.ifoggz-network.xyz\n" << "addnode=seed.gridcoin.pl\n" << "addnode=swe.tplinkdns.com\n" - << "addnode=tarmoilves.eu\n" << "addnode=www.grcpool.com\n"; } From 9b55a47a6515abc503c8c16422bb0a33a9b9c6c9 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 14:51:53 +0300 Subject: [PATCH 072/280] wallet: use std::shuffle instead of std::random_shuffle --- src/wallet/wallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 17152a6383..6b1319e2b0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1532,7 +1532,8 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, vector > > vValue; int64_t nTotalLower = 0; - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + auto seed = static_cast(GetTimeMicros()); + std::shuffle(vCoins.begin(), vCoins.end(), std::default_random_engine(seed)); for (auto output : vCoins) { From a8c0632ad05c1346fab0d2c44ca2e84fc1b6fe20 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 15:48:23 +0300 Subject: [PATCH 073/280] ci: bump version --- ci/test/00_setup_env_native_old.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test/00_setup_env_native_old.sh b/ci/test/00_setup_env_native_old.sh index 47f33089ba..4afc05d32d 100755 --- a/ci/test/00_setup_env_native_old.sh +++ b/ci/test/00_setup_env_native_old.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_old -export DOCKER_NAME_TAG=ubuntu:16.04 +export DOCKER_NAME_TAG=ubuntu:18.04 export PACKAGES="libqt5gui5 libqt5core5a qtbase5-dev libqt5dbus5 qttools5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-iostreams-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libqrencode-dev libzip-dev zlib1g zlib1g-dev libcurl4-openssl-dev" export RUN_UNIT_TESTS=true # export RUN_FUNCTIONAL_TESTS=false From c6233336e525e35c2d5e1d996e3c6302beabc711 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 20:48:23 +0300 Subject: [PATCH 074/280] refactor: replace boost::variant with std::variant --- src/base58.h | 10 ++++++---- src/gridcoin/cpid.cpp | 5 ++--- src/gridcoin/cpid.h | 19 +++++++++---------- src/gridcoin/researcher.cpp | 12 ++++++------ src/gridcoin/researcher.h | 4 +--- src/gridcoin/superblock.cpp | 31 +++++++++++++++---------------- src/gridcoin/superblock.h | 9 ++++----- src/gridcoin/voting/builders.cpp | 4 ++-- src/qt/coincontroldialog.cpp | 24 ++++++++++++++---------- src/rpc/rawtransaction.cpp | 21 ++++++++++++--------- src/script.cpp | 12 ++++++------ src/script.h | 5 +++-- src/test/base58_tests.cpp | 10 +++++----- src/wallet/rpcdump.cpp | 1 - src/wallet/rpcwallet.cpp | 7 ++++--- src/wallet/wallet.cpp | 3 +-- src/wallet/walletdb.cpp | 2 ++ test/lint/lint-includes.sh | 3 --- 18 files changed, 92 insertions(+), 90 deletions(-) diff --git a/src/base58.h b/src/base58.h index 665966351f..f8279b2c77 100644 --- a/src/base58.h +++ b/src/base58.h @@ -15,12 +15,14 @@ #ifndef BITCOIN_BASE58_H #define BITCOIN_BASE58_H -#include -#include #include "bignum.h" #include "key.h" #include "script.h" +#include +#include +#include + static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; // Encode a byte sequence as a base58-encoded string @@ -260,7 +262,7 @@ class CBase58Data * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. */ class CBitcoinAddress; -class CBitcoinAddressVisitor : public boost::static_visitor +class CBitcoinAddressVisitor { private: CBitcoinAddress *addr; @@ -295,7 +297,7 @@ class CBitcoinAddress : public CBase58Data bool Set(const CTxDestination &dest) { - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); + return std::visit(CBitcoinAddressVisitor(this), dest); } bool IsValid() const diff --git a/src/gridcoin/cpid.cpp b/src/gridcoin/cpid.cpp index 3cbf022edd..8e3749e64f 100644 --- a/src/gridcoin/cpid.cpp +++ b/src/gridcoin/cpid.cpp @@ -6,7 +6,6 @@ #include "util.h" #include -#include #include using namespace GRC; @@ -15,7 +14,7 @@ namespace { //! //! \brief Gets the string representation of a mining ID object. //! -struct MiningIdToStringVisitor : boost::static_visitor +struct MiningIdToStringVisitor { //! //! \brief Call the mining ID variant type's \c ToString() method directly. @@ -117,7 +116,7 @@ MiningId MiningId::Parse(const std::string& input) std::string MiningId::ToString() const { - return boost::apply_visitor(MiningIdToStringVisitor(), m_variant); + return std::visit(MiningIdToStringVisitor(), m_variant); } // ----------------------------------------------------------------------------- diff --git a/src/gridcoin/cpid.h b/src/gridcoin/cpid.h index 41da110c26..a5e0018cd8 100644 --- a/src/gridcoin/cpid.h +++ b/src/gridcoin/cpid.h @@ -8,10 +8,9 @@ #include #include -#include -#include #include #include +#include namespace GRC { //! @@ -299,12 +298,12 @@ class MiningId //! bool operator==(const MiningId& other) const { - if (m_variant.which() != other.m_variant.which()) { + if (m_variant.index() != other.m_variant.index()) { return false; } if (Which() == Kind::CPID) { - return *this == boost::get(other.m_variant); + return *this == std::get(other.m_variant); } return true; @@ -333,7 +332,7 @@ class MiningId bool operator==(const Cpid& other) const { return Which() == Kind::CPID - && boost::get(m_variant) == other; + && std::get(m_variant) == other; } //! @@ -356,7 +355,7 @@ class MiningId //! Kind Which() const { - return static_cast(m_variant.which()); + return static_cast(m_variant.index()); } //! @@ -381,7 +380,7 @@ class MiningId return boost::none; } - return boost::get(m_variant); + return std::get(m_variant); } //! @@ -400,12 +399,12 @@ class MiningId template void Serialize(Stream& stream) const { - unsigned char kind = m_variant.which(); + unsigned char kind = m_variant.index(); ::Serialize(stream, kind); if (static_cast(kind) == Kind::CPID) { - boost::get(m_variant).Serialize(stream); + std::get(m_variant).Serialize(stream); } } @@ -443,7 +442,7 @@ class MiningId //! //! \brief Stores the various states that a mining ID may exist in. //! - boost::variant m_variant; + std::variant m_variant; }; // MiningId } // namespace GRC diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index 045ba1a51d..2d4b8f7f96 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -1048,8 +1048,8 @@ AdvertiseBeaconResult::AdvertiseBeaconResult(const BeaconError error) CPubKey* AdvertiseBeaconResult::TryPublicKey() { - if (m_result.which() == 0) { - return &boost::get(m_result); + if (m_result.index() == 0) { + return &std::get(m_result); } return nullptr; @@ -1057,8 +1057,8 @@ CPubKey* AdvertiseBeaconResult::TryPublicKey() const CPubKey* AdvertiseBeaconResult::TryPublicKey() const { - if (m_result.which() == 0) { - return &boost::get(m_result); + if (m_result.index() == 0) { + return &std::get(m_result); } return nullptr; @@ -1066,8 +1066,8 @@ const CPubKey* AdvertiseBeaconResult::TryPublicKey() const BeaconError AdvertiseBeaconResult::Error() const { - if (m_result.which() > 0) { - return boost::get(m_result); + if (m_result.index() > 0) { + return std::get(m_result); } return BeaconError::NONE; diff --git a/src/gridcoin/researcher.h b/src/gridcoin/researcher.h index 31d3e1f15b..65dfa2fcd3 100644 --- a/src/gridcoin/researcher.h +++ b/src/gridcoin/researcher.h @@ -8,8 +8,6 @@ #include "key.h" #include "gridcoin/cpid.h" -#include -#include #include #include #include @@ -309,7 +307,7 @@ class AdvertiseBeaconResult //! \brief Contains the beacon public key if advertisement succeeded or //! the error result if it did not. //! - boost::variant m_result; + std::variant m_result; }; //! diff --git a/src/gridcoin/superblock.cpp b/src/gridcoin/superblock.cpp index cece6622e4..81b2d5744a 100644 --- a/src/gridcoin/superblock.cpp +++ b/src/gridcoin/superblock.cpp @@ -11,7 +11,6 @@ #include "util.h" #include "util/reverse_iterator.h" -#include #include using namespace GRC; @@ -475,7 +474,7 @@ class LegacySuperblockParser //! //! \brief Gets the string representation of a quorum hash object. //! -struct QuorumHashToStringVisitor : boost::static_visitor +struct QuorumHashToStringVisitor { //! //! \brief Get the string representation of an invalid or empty quorum hash. @@ -1016,7 +1015,7 @@ QuorumHash::QuorumHash(const std::vector& bytes) : QuorumHash() m_hash = uint256(bytes); } else if (bytes.size() == sizeof(Md5Sum)) { m_hash = Md5Sum(); - std::copy(bytes.begin(), bytes.end(), boost::get(m_hash).begin()); + std::copy(bytes.begin(), bytes.end(), std::get(m_hash).begin()); } } @@ -1081,7 +1080,7 @@ QuorumHash QuorumHash::Parse(const std::string& hex) bool QuorumHash::operator==(const QuorumHash& other) const { - if (m_hash.which() != other.m_hash.which()) { + if (m_hash.index() != other.m_hash.index()) { return false; } @@ -1090,12 +1089,12 @@ bool QuorumHash::operator==(const QuorumHash& other) const return true; case Kind::SHA256: - return boost::get(m_hash) - == boost::get(other.m_hash); + return std::get(m_hash) + == std::get(other.m_hash); case Kind::MD5: - return boost::get(m_hash) - == boost::get(other.m_hash); + return std::get(m_hash) + == std::get(other.m_hash); } return false; @@ -1109,7 +1108,7 @@ bool QuorumHash::operator!=(const QuorumHash& other) const bool QuorumHash::operator==(const uint256& other) const { return Which() == Kind::SHA256 - && boost::get(m_hash) == other; + && std::get(m_hash) == other; } bool QuorumHash::operator!=(const uint256& other) const @@ -1125,13 +1124,13 @@ bool QuorumHash::operator==(const std::string& other) const case Kind::SHA256: return other.size() == sizeof(uint256) * 2 - && boost::get(m_hash) == uint256S(other); + && std::get(m_hash) == uint256S(other); case Kind::MD5: return other.size() == sizeof(Md5Sum) * 2 && std::equal( - boost::get(m_hash).begin(), - boost::get(m_hash).end(), + std::get(m_hash).begin(), + std::get(m_hash).end(), ParseHex(other).begin()); } @@ -1145,7 +1144,7 @@ bool QuorumHash::operator!=(const std::string&other) const QuorumHash::Kind QuorumHash::Which() const { - return static_cast(m_hash.which()); + return static_cast(m_hash.index()); } bool QuorumHash::Valid() const @@ -1159,9 +1158,9 @@ const unsigned char* QuorumHash::Raw() const case Kind::INVALID: return nullptr; case Kind::SHA256: - return boost::get(m_hash).begin(); + return std::get(m_hash).begin(); case Kind::MD5: - return boost::get(m_hash).data(); + return std::get(m_hash).data(); } return nullptr; @@ -1169,5 +1168,5 @@ const unsigned char* QuorumHash::Raw() const std::string QuorumHash::ToString() const { - return boost::apply_visitor(QuorumHashToStringVisitor(), m_hash); + return std::visit(QuorumHashToStringVisitor(), m_hash); } diff --git a/src/gridcoin/superblock.h b/src/gridcoin/superblock.h index 17400f20a6..90b099d02d 100644 --- a/src/gridcoin/superblock.h +++ b/src/gridcoin/superblock.h @@ -11,7 +11,6 @@ #include "uint256.h" #include -#include #include #include #include @@ -171,7 +170,7 @@ class QuorumHash template void Serialize(Stream& stream) const { - unsigned char kind = m_hash.which(); + unsigned char kind = m_hash.index(); ::Serialize(stream, kind); @@ -180,11 +179,11 @@ class QuorumHash break; // Suppress warning. case Kind::SHA256: - boost::get(m_hash).Serialize(stream); + std::get(m_hash).Serialize(stream); break; case Kind::MD5: { - const Md5Sum& hash = boost::get(m_hash); + const Md5Sum& hash = std::get(m_hash); stream.write(CharCast(hash.data()), hash.size()); break; @@ -234,7 +233,7 @@ class QuorumHash //! CONSENSUS: Do not remove or reorder the types in this variant. This //! class relies on the type ordinality to tag serialized values. //! - boost::variant m_hash; + std::variant m_hash; }; // QuorumHash //! diff --git a/src/gridcoin/voting/builders.cpp b/src/gridcoin/voting/builders.cpp index a539cf0a8f..0bfa71ce86 100644 --- a/src/gridcoin/voting/builders.cpp +++ b/src/gridcoin/voting/builders.cpp @@ -147,7 +147,7 @@ class CoinPicker //! \brief Adds outputs for an address only when an address represents //! a pay-to-public-key or pay-to-public-key-hash destination. //! - class PubkeyDestinationFilter : public boost::static_visitor + class PubkeyDestinationFilter { public: //! @@ -232,7 +232,7 @@ class CoinPicker // more advanced redemption scripts like multisig yet: // auto filter = std::bind(pubkey_filter, std::placeholders::_1, txo); - boost::apply_visitor(filter, dest); + std::visit(filter, dest); } return by_address; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b297a1a45f..e6919488aa 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -464,14 +464,17 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // Bytes CTxDestination address; - if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { CPubKey pubkey; - CKeyID *keyid = boost::get< CKeyID >(&address); - if (keyid && model->getPubKey(*keyid, pubkey)) - nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); - else - nBytesInputs += 148; // in all error cases, simply assume 148 here + try { + if (model->getPubKey(std::get(address), pubkey)) + nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); + else + nBytesInputs += 148; // in all error cases, simply assume 148 here + } catch (const std::bad_variant_access&) { + nBytesInputs += 148; + } } else nBytesInputs += 148; } @@ -636,7 +639,7 @@ void CoinControlDialog::updateView() // address CTxDestination outputAddress; QString sAddress = ""; - if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) { sAddress = CBitcoinAddress(outputAddress).ToString().c_str(); @@ -645,9 +648,10 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_ADDRESS, sAddress); CPubKey pubkey; - CKeyID *keyid = boost::get< CKeyID >(&outputAddress); - if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) - nInputSize = 180; + try { + if (model->getPubKey(std::get(outputAddress), pubkey) && !pubkey.IsCompressed()) + nInputSize = 180; + } catch (const std::bad_variant_access&) {} } // label diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 84d54f8355..5ef4f1f4fb 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -711,17 +711,20 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) // For fee calculation. This is similar to the calculation in coincontroldialog.cpp. CTxDestination address; - if(ExtractDestination(out.second.tx->vout[out.second.i].scriptPubKey, address)) + if (ExtractDestination(out.second.tx->vout[out.second.i].scriptPubKey, address)) { CPubKey pubkey; - CKeyID *keyid = boost::get(&address); - if (keyid && pwalletMain->GetPubKey(*keyid, pubkey)) - { - nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); - } - // in all error cases, simply assume 148 here - else - { + try { + if (pwalletMain->GetPubKey(std::get(address), pubkey)) + { + nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); + } + // in all error cases, simply assume 148 here + else + { + nBytesInputs += 148; + } + } catch (const std::bad_variant_access&) { nBytesInputs += 148; } } diff --git a/src/script.cpp b/src/script.cpp index 65a4eb3aab..2521507514 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1533,7 +1533,7 @@ unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) return nResult; } -class CKeyStoreIsMineVisitor : public boost::static_visitor +class CKeyStoreIsMineVisitor { private: const CKeyStore *keystore; @@ -1546,7 +1546,7 @@ class CKeyStoreIsMineVisitor : public boost::static_visitor isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest) { - if (boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest)) + if (std::visit(CKeyStoreIsMineVisitor(&keystore), dest)) { return ISMINE_SPENDABLE; } @@ -1648,7 +1648,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) return false; } -class CAffectedKeysVisitor : public boost::static_visitor { +class CAffectedKeysVisitor { private: const CKeyStore &keystore; std::vector &vKeys; @@ -1662,7 +1662,7 @@ class CAffectedKeysVisitor : public boost::static_visitor { int nRequired; if (ExtractDestinations(script, type, vDest, nRequired)) { for (auto const &dest : vDest) - boost::apply_visitor(*this, dest); + std::visit(*this, dest); } } @@ -2023,7 +2023,7 @@ bool CScript::HasCanonicalPushes() const return true; } -class CScriptVisitor : public boost::static_visitor +class CScriptVisitor { private: CScript *script; @@ -2050,7 +2050,7 @@ class CScriptVisitor : public boost::static_visitor void CScript::SetDestination(const CTxDestination& dest) { - boost::apply_visitor(CScriptVisitor(this), dest); + std::visit(CScriptVisitor(this), dest); } void CScript::SetMultisig(int nRequired, const std::vector& keys) diff --git a/src/script.h b/src/script.h index 2b0043d1f8..ceb22cecae 100644 --- a/src/script.h +++ b/src/script.h @@ -7,11 +7,11 @@ #define H_BITCOIN_SCRIPT #include +#include #include #include -#include #include "keystore.h" #include "bignum.h" @@ -72,6 +72,7 @@ enum txnouttype class CNoDestination { public: friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } + friend bool operator!=(const CNoDestination &a, const CNoDestination &b) { return false; } friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; @@ -81,7 +82,7 @@ class CNoDestination { * * CScriptID: TX_SCRIPTHASH destination * A CTxDestination is the internal data type encoded in a CBitcoinAddress */ -typedef boost::variant CTxDestination; +typedef std::variant CTxDestination; const char* GetTxnOutputType(txnouttype t); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 92998d3446..71dcd9437d 100755 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) } // Visitor to check address type -class TestAddrTypeVisitor : public boost::static_visitor +class TestAddrTypeVisitor { private: std::string exp_addrType; @@ -79,7 +79,7 @@ class TestAddrTypeVisitor : public boost::static_visitor }; // Visitor to check address payload -class TestPayloadVisitor : public boost::static_visitor +class TestPayloadVisitor { private: std::vector exp_payload; @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); CTxDestination dest = addr.Get(); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); + BOOST_CHECK_MESSAGE(std::visit(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); // Public key must be invalid private key secret.SetString(exp_base58string); @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) continue; } CBitcoinAddress addrOut; - BOOST_CHECK_MESSAGE(boost::apply_visitor(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest); + BOOST_CHECK_MESSAGE(std::visit(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest); BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest + addrOut.ToString()); } } @@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) // Visiting a CNoDestination must fail CBitcoinAddress dummyAddr; CTxDestination nodest = CNoDestination(); - BOOST_CHECK(!boost::apply_visitor(CBitcoinAddressVisitor(&dummyAddr), nodest)); + BOOST_CHECK(!std::visit(CBitcoinAddressVisitor(&dummyAddr), nodest)); // Restore global state fTestNet = fTestNet_stored; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 66711a26dc..f76261794f 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -10,7 +10,6 @@ #include "base58.h" #include -#include #include using namespace std; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e4b46f6dab..9be81c5933 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -20,6 +20,7 @@ #include "wallet/ismine.h" #include +#include using namespace std; @@ -2294,7 +2295,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) return "wallet encrypted; Gridcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } -class DescribeAddressVisitor : public boost::static_visitor +class DescribeAddressVisitor { public: UniValue operator()(const CNoDestination &dest) const { return UniValue(); } @@ -2353,7 +2354,7 @@ UniValue validateaddress(const UniValue& params, bool fHelp) bool fMine = IsMine(*pwalletMain, dest) != ISMINE_NO; ret.pushKV("ismine", fMine); if (fMine) { - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + UniValue detail = std::visit(DescribeAddressVisitor(), dest); ret.pushKVs(detail); } if (pwalletMain->mapAddressBook.count(dest)) @@ -2394,7 +2395,7 @@ UniValue validatepubkey(const UniValue& params, bool fHelp) ret.pushKV("ismine", fMine); ret.pushKV("iscompressed", isCompressed); if (fMine) { - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + UniValue detail = std::visit(DescribeAddressVisitor(), dest); ret.pushKVs(detail); } if (pwalletMain->mapAddressBook.count(dest)) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6b1319e2b0..936df1d14f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -15,7 +15,6 @@ #include "rpc/server.h" #include "rpc/client.h" #include "rpc/protocol.h" -#include #include #include "main.h" #include "util.h" @@ -1967,7 +1966,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CScript scriptChange; // coin control: send change to custom address - if (coinControl && !boost::get(&coinControl->destChange)) + if (coinControl && !std::get_if(&coinControl->destChange)) scriptChange.SetDestination(coinControl->destChange); // no coin control: send change to newly generated address diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4c3f9a5518..eaaf7313ba 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -8,6 +8,8 @@ #include "wallet/wallet.h" #include "init.h" +#include + using namespace std; static uint64_t nAccountingEntryNumber = 0; diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 4c872564a9..12f1a9656e 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -72,9 +72,6 @@ EXPECTED_BOOST_INCLUDES=( boost/thread/mutex.hpp boost/thread/shared_mutex.hpp boost/thread/thread.hpp - boost/variant.hpp - boost/variant/apply_visitor.hpp - boost/variant/static_visitor.hpp ) #for BOOST_INCLUDE in $(git grep '^#include ' | sort -u); do From e8dfebc6250effdc8742defee9f4f4c84536f9d4 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 24 Apr 2021 23:09:51 +0300 Subject: [PATCH 075/280] refactor: replace boost::optional with std::optional --- src/gridcoin/account.h | 8 ++++---- src/gridcoin/claim.h | 2 +- src/gridcoin/contract/contract.h | 2 +- src/gridcoin/cpid.h | 6 +++--- src/gridcoin/quorum.cpp | 6 +++--- src/gridcoin/researcher.cpp | 30 +++++++++++++++--------------- src/gridcoin/researcher.h | 4 ++-- src/gridcoin/staking/chain_trust.h | 4 ++-- src/gridcoin/staking/kernel.cpp | 2 +- src/gridcoin/superblock.cpp | 2 +- src/gridcoin/superblock.h | 8 ++++---- src/gridcoin/voting/builders.cpp | 18 +++++++++--------- src/gridcoin/voting/fwd.h | 5 ++--- src/gridcoin/voting/poll.cpp | 4 ++-- src/gridcoin/voting/poll.h | 2 +- src/gridcoin/voting/registry.cpp | 4 ++-- src/gridcoin/voting/result.cpp | 4 ++-- src/main.cpp | 2 +- src/main.h | 2 +- src/miner.cpp | 6 +++--- src/miner.h | 3 +-- src/rpc/mining.cpp | 4 ++-- src/test/gridcoin/claim_tests.cpp | 8 ++++---- src/ui_interface.h | 4 ++-- test/lint/lint-includes.sh | 1 - 25 files changed, 69 insertions(+), 72 deletions(-) diff --git a/src/gridcoin/account.h b/src/gridcoin/account.h index bb936547e4..86df8ede24 100644 --- a/src/gridcoin/account.h +++ b/src/gridcoin/account.h @@ -5,7 +5,7 @@ #pragma once #include "amount.h" -#include +#include #include class CBlockIndex; @@ -24,7 +24,7 @@ typedef std::unordered_map ResearchAccountMap; //! \brief An optional type that contains a pointer to a block index object or //! does not. //! -typedef boost::optional BlockPtrOption; +typedef std::optional BlockPtrOption; //! //! \brief Stores the research reward context for a CPID used to calculate @@ -105,7 +105,7 @@ class ResearchAccount BlockPtrOption FirstRewardBlock() const { if (m_first_block_ptr == nullptr) { - return boost::none; + return std::nullopt; } return BlockPtrOption(m_first_block_ptr); @@ -168,7 +168,7 @@ class ResearchAccount BlockPtrOption LastRewardBlock() const { if (m_last_block_ptr == nullptr) { - return boost::none; + return std::nullopt; } return BlockPtrOption(m_last_block_ptr); diff --git a/src/gridcoin/claim.h b/src/gridcoin/claim.h index 53ea268520..d408af48b7 100644 --- a/src/gridcoin/claim.h +++ b/src/gridcoin/claim.h @@ -11,7 +11,7 @@ #include "serialize.h" #include "uint256.h" -#include +#include class CPubKey; diff --git a/src/gridcoin/contract/contract.h b/src/gridcoin/contract/contract.h index a1e30781f3..8bc8a08bf7 100644 --- a/src/gridcoin/contract/contract.h +++ b/src/gridcoin/contract/contract.h @@ -9,7 +9,7 @@ #include "gridcoin/support/enumbytes.h" #include "serialize.h" -#include +#include #include #include diff --git a/src/gridcoin/cpid.h b/src/gridcoin/cpid.h index a5e0018cd8..033b7e767b 100644 --- a/src/gridcoin/cpid.h +++ b/src/gridcoin/cpid.h @@ -7,7 +7,7 @@ #include "serialize.h" #include -#include +#include #include #include #include @@ -208,7 +208,7 @@ class Cpid //! \brief An optional type that either contains a reference to some external //! CPID value or does not. //! -typedef boost::optional CpidOption; +typedef std::optional CpidOption; //! //! \brief A variant type that identifies an entity that may receive rewards. @@ -377,7 +377,7 @@ class MiningId CpidOption TryCpid() const { if (Which() != Kind::CPID) { - return boost::none; + return std::nullopt; } return std::get(m_variant); diff --git a/src/gridcoin/quorum.cpp b/src/gridcoin/quorum.cpp index 5ce12ff635..8b6fbb3623 100644 --- a/src/gridcoin/quorum.cpp +++ b/src/gridcoin/quorum.cpp @@ -927,10 +927,10 @@ class SuperblockValidator //! \return A convergence to generate a superblock hash from if a new //! combination is possible. //! - boost::optional GetNextConvergence() + std::optional GetNextConvergence() { if (m_current_combination == m_total_combinations) { - return boost::none; + return std::nullopt; } ConvergenceCandidate convergence; @@ -971,7 +971,7 @@ class SuperblockValidator ++m_current_combination; - return boost::make_optional(std::move(convergence)); + return std::make_optional(std::move(convergence)); } private: diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index 2d4b8f7f96..d895fc2f46 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -269,13 +269,13 @@ bool IsPoolUsername(const std::string& username) //! //! \return The entire client_state.xml file as a string if readable. //! -boost::optional ReadClientStateXml() +std::optional ReadClientStateXml() { const fs::path path = GetBoincDataDir(); std::string contents = GetFileContents(path / "client_state.xml"); if (contents != "-1") { - return boost::make_optional(std::move(contents)); + return std::make_optional(std::move(contents)); } LogPrintf("WARNING: Unable to obtain BOINC CPIDs."); @@ -288,7 +288,7 @@ boost::optional ReadClientStateXml() "Please specify its current location in gridcoinresearch.conf."); } - return boost::none; + return std::nullopt; } //! @@ -301,7 +301,7 @@ boost::optional ReadClientStateXml() //! std::vector FetchProjectsXml() { - const boost::optional client_state_xml = ReadClientStateXml(); + const std::optional client_state_xml = ReadClientStateXml(); if (!client_state_xml) { return { }; @@ -431,12 +431,12 @@ void TryProjectCpid(MiningId& mining_id, const MiningProject& project) //! \return The computed external CPID if the hash of the configured email //! address matches the supplied email address hash. //! -boost::optional FallbackToCpidByEmail( +std::optional FallbackToCpidByEmail( const std::string& email_hash, const std::string& internal_cpid) { if (email_hash.empty() || internal_cpid.empty()) { - return boost::none; + return std::nullopt; } const std::string email = Researcher::Email(); @@ -447,7 +447,7 @@ boost::optional FallbackToCpidByEmail( email_hash_bytes.data()); if (HexStr(email_hash_bytes) != email_hash) { - return boost::none; + return std::nullopt; } return Cpid::Hash(internal_cpid, email); @@ -868,7 +868,7 @@ MiningProject MiningProject::Parse(const std::string& xml) // CPID of the project from the internal CPID and email address: // if (external_cpid.empty()) { - if (const boost::optional cpid = FallbackToCpidByEmail( + if (const std::optional cpid = FallbackToCpidByEmail( ExtractXML(xml, "", ""), ExtractXML(xml, "", ""))) { @@ -1344,12 +1344,12 @@ ResearcherStatus Researcher::Status() const return ResearcherStatus::INVESTOR; } -boost::optional Researcher::TryBeacon() const +std::optional Researcher::TryBeacon() const { const CpidOption cpid = m_mining_id.TryCpid(); if (!cpid) { - return boost::none; + return std::nullopt; } LOCK(cs_main); @@ -1357,18 +1357,18 @@ boost::optional Researcher::TryBeacon() const const BeaconOption beacon = GetBeaconRegistry().Try(*cpid); if (!beacon) { - return boost::none; + return std::nullopt; } return *beacon; } -boost::optional Researcher::TryPendingBeacon() const +std::optional Researcher::TryPendingBeacon() const { const CpidOption cpid = m_mining_id.TryCpid(); if (!cpid) { - return boost::none; + return std::nullopt; } LOCK(cs_main); @@ -1376,7 +1376,7 @@ boost::optional Researcher::TryPendingBeacon() const const PendingBeacon* pending_beacon = g_recent_beacons.Try(*cpid); if (!pending_beacon) { - return boost::none; + return std::nullopt; } return *pending_beacon; diff --git a/src/gridcoin/researcher.h b/src/gridcoin/researcher.h index 65dfa2fcd3..cfa68e262c 100644 --- a/src/gridcoin/researcher.h +++ b/src/gridcoin/researcher.h @@ -513,14 +513,14 @@ class Researcher //! //! \return Contains the beacon for the CPID or does not. //! - boost::optional TryBeacon() const; + std::optional TryBeacon() const; //! //! \brief Get the pending beacon for the current CPID if it exists. //! //! \return Contains the pending beacon for the CPID or does not. //! - boost::optional TryPendingBeacon() const; + std::optional TryPendingBeacon() const; //! //! \brief Get the error from the last beacon advertisement, if any. diff --git a/src/gridcoin/staking/chain_trust.h b/src/gridcoin/staking/chain_trust.h index a6825aea6f..2c9e3c15f2 100644 --- a/src/gridcoin/staking/chain_trust.h +++ b/src/gridcoin/staking/chain_trust.h @@ -195,7 +195,7 @@ class ChainTrustCache //! //! \return Either contains a trust value for the block or does not. //! - boost::optional Try(const CBlockIndex* const pindex) const + std::optional Try(const CBlockIndex* const pindex) const { for (size_t i = 0; i < SIZE; ++i) { if (m_index_cache[i] == pindex) { @@ -203,7 +203,7 @@ class ChainTrustCache } } - return boost::none; + return std::nullopt; } //! diff --git a/src/gridcoin/staking/kernel.cpp b/src/gridcoin/staking/kernel.cpp index b1d256b0d2..8939bec462 100644 --- a/src/gridcoin/staking/kernel.cpp +++ b/src/gridcoin/staking/kernel.cpp @@ -33,7 +33,7 @@ class StakeModifierCandidate //! can potentially compare this hash many times. By caching the hash value //! here, we avoid recomputing it for each iteration of the selection loop. //! - mutable boost::optional m_selection_hash; + mutable std::optional m_selection_hash; //! //! \brief Initialize a new candidate entry. diff --git a/src/gridcoin/superblock.cpp b/src/gridcoin/superblock.cpp index 81b2d5744a..4731297f2c 100644 --- a/src/gridcoin/superblock.cpp +++ b/src/gridcoin/superblock.cpp @@ -899,7 +899,7 @@ Superblock::ProjectIndex::Try(const std::string& name) const [](const ProjectPair& a, const std::string& b) { return a.first < b; }); if (iter == m_projects.end() || iter->first != name) { - return boost::none; + return std::nullopt; } return iter->second; diff --git a/src/gridcoin/superblock.h b/src/gridcoin/superblock.h index 90b099d02d..9d40d97160 100644 --- a/src/gridcoin/superblock.h +++ b/src/gridcoin/superblock.h @@ -10,7 +10,7 @@ #include "serialize.h" #include "uint256.h" -#include +#include #include #include #include @@ -358,7 +358,7 @@ class Superblock //! //! \return The CPID's magnitude at normal scale. //! - boost::optional MagnitudeOf(const Cpid& cpid) const + std::optional MagnitudeOf(const Cpid& cpid) const { const auto iter = std::lower_bound( m_magnitudes.begin(), @@ -367,7 +367,7 @@ class Superblock CompareCpidOfPairLessThan); if (iter == m_magnitudes.end() || iter->first != cpid) { - return boost::none; + return std::nullopt; } return Magnitude::FromScaled(iter->second * Scale); @@ -1000,7 +1000,7 @@ class Superblock //! \brief An optional type that either contains some project statistics or //! does not. //! - typedef boost::optional ProjectStatsOption; + typedef std::optional ProjectStatsOption; //! //! \brief Contains aggregated project statistics. diff --git a/src/gridcoin/voting/builders.cpp b/src/gridcoin/voting/builders.cpp index 0bfa71ce86..30acae1cfb 100644 --- a/src/gridcoin/voting/builders.cpp +++ b/src/gridcoin/voting/builders.cpp @@ -293,7 +293,7 @@ class AddressClaimBuilder //! //! \return An address claim for the provided outputs. //! - boost::optional TryBuildClaim(AddressOutputs address_outputs) const + std::optional TryBuildClaim(AddressOutputs address_outputs) const { AddressClaim claim(std::move(address_outputs.m_outpoints)); @@ -302,7 +302,7 @@ class AddressClaimBuilder __func__, DestinationToAddressString(address_outputs.m_key_id)); - return boost::none; + return std::nullopt; } // An address claim must submit outputs in ascending order. This @@ -493,7 +493,7 @@ class MagnitudeClaimBuilder return; } - const boost::optional beacon = m_researcher->TryBeacon(); + const std::optional beacon = m_researcher->TryBeacon(); // Avoid building a claim for a beacon that will expire soon: if (!beacon || beacon->Expired(GetAdjustedTime() + 15 * 60)) { @@ -501,7 +501,7 @@ class MagnitudeClaimBuilder return; } - const boost::optional beacon_txid = FindBeaconTxid(*beacon); + const std::optional beacon_txid = FindBeaconTxid(*beacon); if (!beacon_txid) { LogPrint(LogFlags::VOTE, "%s: beacon tx not found", __func__); @@ -527,7 +527,7 @@ class MagnitudeClaimBuilder return true; } - const boost::optional beacon = m_researcher->TryBeacon(); + const std::optional beacon = m_researcher->TryBeacon(); if (!beacon) { // Should never happen: @@ -569,7 +569,7 @@ class MagnitudeClaimBuilder //! //! \return The hash of the transaction for the beacon contract if found. //! - boost::optional FindBeaconTxid(const Beacon& beacon) const + std::optional FindBeaconTxid(const Beacon& beacon) const { // TODO: This is rather slow, but we only need to do it once per vote. // Store a reference to a wallet's beacon transactions and rewrite the @@ -578,7 +578,7 @@ class MagnitudeClaimBuilder const CBlockIndex* pindex = pindexBest; if (!pindex) { - return boost::none; + return std::nullopt; } const int64_t max_time = FutureDrift(beacon.m_timestamp, 0); @@ -611,7 +611,7 @@ class MagnitudeClaimBuilder } } - return boost::none; + return std::nullopt; } }; // MagnitudeClaimBuilder @@ -1206,7 +1206,7 @@ VoteBuilder VoteBuilder::AddResponse(const uint8_t offset) VoteBuilder VoteBuilder::AddResponse(const std::string& label) { - if (boost::optional offset = m_poll->Choices().OffsetOf(label)) { + if (std::optional offset = m_poll->Choices().OffsetOf(label)) { return AddResponse(*offset); } diff --git a/src/gridcoin/voting/fwd.h b/src/gridcoin/voting/fwd.h index 99a29fb49c..ed7c136ce7 100644 --- a/src/gridcoin/voting/fwd.h +++ b/src/gridcoin/voting/fwd.h @@ -6,7 +6,6 @@ #include "amount.h" -#include namespace GRC { @@ -15,8 +14,8 @@ class PollReference; class PollResult; class Vote; -using PollOption = boost::optional; -using PollResultOption = boost::optional; +using PollOption = std::optional; +using PollResultOption = std::optional; //! //! \brief The unspent amount that a poll creator must hold in an address. diff --git a/src/gridcoin/voting/poll.cpp b/src/gridcoin/voting/poll.cpp index e185943efe..68ff722279 100644 --- a/src/gridcoin/voting/poll.cpp +++ b/src/gridcoin/voting/poll.cpp @@ -291,7 +291,7 @@ bool ChoiceList::LabelExists(const std::string& label) const return OffsetOf(label).operator bool(); } -boost::optional ChoiceList::OffsetOf(const std::string& label) const +std::optional ChoiceList::OffsetOf(const std::string& label) const { const auto iter = std::find_if( m_choices.begin(), @@ -299,7 +299,7 @@ boost::optional ChoiceList::OffsetOf(const std::string& label) const [&](const Choice& choice) { return choice.m_label == label; }); if (iter == m_choices.end()) { - return boost::none; + return std::nullopt; } return std::distance(m_choices.begin(), iter); diff --git a/src/gridcoin/voting/poll.h b/src/gridcoin/voting/poll.h index fcec48f0bf..bc62ff4a12 100644 --- a/src/gridcoin/voting/poll.h +++ b/src/gridcoin/voting/poll.h @@ -169,7 +169,7 @@ class Poll //! \return An object that either contains the offset of the label or //! does not when no choice contains a matching label. //! - boost::optional OffsetOf(const std::string& label) const; + std::optional OffsetOf(const std::string& label) const; //! //! \brief Get the poll choice at the specified offset. diff --git a/src/gridcoin/voting/registry.cpp b/src/gridcoin/voting/registry.cpp index dc24f89464..4eb5ff24b6 100644 --- a/src/gridcoin/voting/registry.cpp +++ b/src/gridcoin/voting/registry.cpp @@ -241,7 +241,7 @@ PollOption PollReference::TryReadFromDisk(CTxDB& txdb) const if (!txdb.ReadDiskTx(*m_ptxid, tx)) { error("%s: failed to read poll tx from disk", __func__); - return boost::none; + return std::nullopt; } for (auto& contract : tx.PullContracts()) { @@ -255,7 +255,7 @@ PollOption PollReference::TryReadFromDisk(CTxDB& txdb) const error("%s: transaction does not contain a poll contract", __func__); - return boost::none; + return std::nullopt; } PollOption PollReference::TryReadFromDisk() const diff --git a/src/gridcoin/voting/result.cpp b/src/gridcoin/voting/result.cpp index 173810838d..4582d1c128 100644 --- a/src/gridcoin/voting/result.cpp +++ b/src/gridcoin/voting/result.cpp @@ -13,7 +13,7 @@ #include "txdb.h" #include "util/reverse_iterator.h" -#include +#include #include #include @@ -1091,7 +1091,7 @@ PollResultOption PollResult::BuildFor(const PollReference& poll_ref) return result; } - return boost::none; + return std::nullopt; } size_t PollResult::Winner() const diff --git a/src/main.cpp b/src/main.cpp index c09332dd81..a37f4d9338 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4214,7 +4214,7 @@ GRC::ClaimOption GetClaimByIndex(const CBlockIndex* const pblockindex) if (!pblockindex || !pblockindex->IsInMainChain() || !block.ReadFromDisk(pblockindex)) { - return boost::none; + return std::nullopt; } return block.PullClaim(); diff --git a/src/main.h b/src/main.h index 572d79ee4e..84226720e5 100644 --- a/src/main.h +++ b/src/main.h @@ -44,7 +44,7 @@ class SuperblockPtr; //! //! \brief An optional type that either contains some claim object or does not. //! -typedef boost::optional ClaimOption; +typedef std::optional ClaimOption; } static const int64_t DEFAULT_CBR = 10 * COIN; diff --git a/src/miner.cpp b/src/miner.cpp index b01fba0f43..2691c82521 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -193,7 +193,7 @@ class TxPriorityCompare } }; -boost::optional GetLastStake(CWallet& wallet) +std::optional GetLastStake(CWallet& wallet) { CWalletTx stake_tx; uint256 cached_stake_tx_hash; @@ -217,7 +217,7 @@ boost::optional GetLastStake(CWallet& wallet) LOCK2(cs_main, wallet.cs_wallet); if (wallet.mapWallet.empty()) { - return boost::none; + return std::nullopt; } auto latest_iter = wallet.mapWallet.cbegin(); @@ -233,7 +233,7 @@ boost::optional GetLastStake(CWallet& wallet) if (latest_iter == wallet.mapWallet.cbegin() && !is_my_confirmed_stake(latest_iter->second)) { - return boost::none; + return std::nullopt; } cached_stake_tx_hash = latest_iter->first; diff --git a/src/miner.h b/src/miner.h index d851291d4d..343920bea5 100644 --- a/src/miner.h +++ b/src/miner.h @@ -8,7 +8,6 @@ #include "main.h" -#include class CWallet; class CWalletTx; @@ -22,7 +21,7 @@ extern unsigned int nMinerSleep; // It will be converted to Halfords in GetNumberOfStakeOutputs by multiplying by COIN. static const int64_t MIN_STAKE_SPLIT_VALUE_GRC = 800; -boost::optional GetLastStake(CWallet& wallet); +std::optional GetLastStake(CWallet& wallet); void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStakeSplit, bool &fEnableSideStaking, SideStakeAlloc &vSideStakeAlloc, double &dEfficiency); unsigned int GetNumberOfStakeOutputs(int64_t &nValue, int64_t &nMinStakeSplitValue, double &dEfficiency); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 35d66dac27..5deb0e517b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -174,7 +174,7 @@ UniValue getlaststake(const UniValue& params, bool fHelp) "\n" "Fetch information about this wallet's last staked block.\n"); - const boost::optional stake_tx = GetLastStake(*pwalletMain); + const std::optional stake_tx = GetLastStake(*pwalletMain); if (!stake_tx) { throw JSONRPCError(RPC_WALLET_ERROR, "No prior staked blocks found."); @@ -384,7 +384,7 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) const AccrualSnapshot snapshot = AccrualSnapshotReader(snapshot_path).Read(); int64_t accrual = 0; - auto entry = snapshot.m_records.find(cpid.get()); + auto entry = snapshot.m_records.find(cpid.value()); if (entry != snapshot.m_records.end()) { diff --git a/src/test/gridcoin/claim_tests.cpp b/src/test/gridcoin/claim_tests.cpp index 69c69efe4d..7cc4c56bfb 100644 --- a/src/test/gridcoin/claim_tests.cpp +++ b/src/test/gridcoin/claim_tests.cpp @@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(it_signs_itself_with_the_supplied_beacon_private_key) BOOST_CHECK(claim.Sign(private_key, last_block_hash, coinstake_tx) == true); - GRC::Cpid cpid = claim.m_mining_id.TryCpid().get(); + GRC::Cpid cpid = claim.m_mining_id.TryCpid().value(); const uint256 hashed = (CHashWriter(SER_NETWORK, PROTOCOL_VERSION) << cpid @@ -310,7 +310,7 @@ BOOST_AUTO_TEST_CASE(it_signs_a_v2_claim_with_the_supplied_beacon_private_key) BOOST_CHECK(claim.Sign(private_key, last_block_hash, coinstake_tx) == true); - GRC::Cpid cpid = claim.m_mining_id.TryCpid().get(); + GRC::Cpid cpid = claim.m_mining_id.TryCpid().value(); const uint256 hashed = Hash( cpid.Raw().begin(), @@ -355,7 +355,7 @@ BOOST_AUTO_TEST_CASE(it_verifies_a_signature_for_a_research_reward_claim) const CTransaction coinstake_tx; CKey private_key = GetTestPrivateKey(); - GRC::Cpid cpid = claim.m_mining_id.TryCpid().get(); + GRC::Cpid cpid = claim.m_mining_id.TryCpid().value(); const uint256 hashed = (CHashWriter(SER_NETWORK, PROTOCOL_VERSION) << cpid @@ -380,7 +380,7 @@ BOOST_AUTO_TEST_CASE(it_verifies_a_signature_for_a_v2_research_reward_claim) const CTransaction coinstake_tx; CKey private_key = GetTestPrivateKey(); - GRC::Cpid cpid = claim.m_mining_id.TryCpid().get(); + GRC::Cpid cpid = claim.m_mining_id.TryCpid().value(); const uint256 hashed = Hash( cpid.Raw().begin(), diff --git a/src/ui_interface.h b/src/ui_interface.h index a1b285af0f..66dc144f5e 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -121,12 +121,12 @@ class CClientUIInterface extern CClientUIInterface uiInterface; /** - * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. + * Translation function: Call Translate signal on UI interface, which returns a std::optional result. * If no translation slot is registered, nothing is returned, and simply return the input. */ inline std::string _(const char* psz) { - boost::optional rv = uiInterface.Translate(psz); + auto rv = uiInterface.Translate(psz); return rv ? (*rv) : psz; } diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 12f1a9656e..7a0bc43334 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -60,7 +60,6 @@ EXPECTED_BOOST_INCLUDES=( boost/multi_index/ordered_index.hpp boost/multi_index/sequenced_index.hpp boost/multi_index_container.hpp - boost/optional.hpp boost/preprocessor/cat.hpp boost/preprocessor/stringize.hpp boost/process.hpp From a142fd7564e884521ae5f70233a7d2072a3ecdd8 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 24 Apr 2021 12:28:48 -0400 Subject: [PATCH 076/280] Extend coin control to implement filter and consolidate functions 1. Changes the (un)select all button to a true select all/select none filp-flop. 2. Implements an input filter function to allow the selection of inputs either less than or equal to or greater than or equal to a provided input value. This can be used for more than just consolidations. 3. Implements a "Consolidate" button which limits the selected inputs to 600 to ensure the consolidation transaction will be successful, presents a pick list to select the destination address for the consolidation, and then automatically fills in the send transaction for convenience. --- src/Makefile.qt.include | 4 + src/qt/coincontroldialog.cpp | 284 ++++++++++++++++++-- src/qt/coincontroldialog.h | 25 +- src/qt/consolidateunspentdialog.cpp | 83 ++++++ src/qt/consolidateunspentdialog.h | 38 +++ src/qt/forms/coincontroldialog.ui | 235 ++++++++++------ src/qt/forms/consolidateunspentdialog.ui | 132 +++++++++ src/qt/res/stylesheets/dark_stylesheet.qss | 6 + src/qt/res/stylesheets/light_stylesheet.qss | 6 + src/qt/sendcoinsdialog.cpp | 23 +- src/qt/sendcoinsdialog.h | 5 +- 11 files changed, 736 insertions(+), 105 deletions(-) create mode 100644 src/qt/consolidateunspentdialog.cpp create mode 100644 src/qt/consolidateunspentdialog.h create mode 100644 src/qt/forms/consolidateunspentdialog.ui diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 024b1099cc..a9a3b824c3 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -74,6 +74,7 @@ QT_TS = \ QT_FORMS_UI = \ qt/forms/aboutdialog.ui \ qt/forms/coincontroldialog.ui \ + qt/forms/consolidateunspentdialog.ui \ qt/forms/diagnosticsdialog.ui \ qt/forms/optionsdialog.ui \ qt/forms/rpcconsole.ui \ @@ -111,6 +112,7 @@ QT_MOC_CPP = \ qt/moc_clientmodel.cpp \ qt/moc_coincontroldialog.cpp \ qt/moc_coincontroltreewidget.cpp \ + qt/moc_consolidateunspentdialog.cpp \ qt/moc_csvmodelwriter.cpp \ qt/moc_diagnosticsdialog.cpp \ qt/moc_editaddressdialog.cpp \ @@ -181,6 +183,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/clientmodel.h \ qt/coincontroldialog.h \ qt/coincontroltreewidget.h \ + qt/consolidateunspentdialog.h \ qt/csvmodelwriter.h \ qt/decoration.h \ qt/diagnosticsdialog.h \ @@ -243,6 +246,7 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/clientmodel.cpp \ qt/coincontroldialog.cpp \ qt/coincontroltreewidget.cpp \ + qt/consolidateunspentdialog.cpp \ qt/csvmodelwriter.cpp \ qt/decoration.cpp \ qt/diagnosticsdialog.cpp \ diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b297a1a45f..d4bbdf2a40 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -3,12 +3,12 @@ #include "init.h" #include "bitcoinunits.h" -#include "walletmodel.h" #include "addresstablemodel.h" #include "optionsmodel.h" #include "policy/fees.h" #include "validation.h" #include "wallet/coincontrol.h" +#include "consolidateunspentdialog.h" #include #include @@ -29,6 +29,7 @@ CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); CoinControlDialog::CoinControlDialog(QWidget *parent) : QDialog(parent), + m_inputSelectionLimit(600), ui(new Ui::CoinControlDialog), model(0) { @@ -106,17 +107,38 @@ CoinControlDialog::CoinControlDialog(QWidget *parent) : // (un)select all connect(ui->selectAllPushButton, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked())); - ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84); - ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100); - ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170); + // filter/consolidate button interaction + connect(ui->maxMinOutputValue, SIGNAL(textChanged()), this, SLOT(maxMinOutputValueChanged())); + + // filter mode + connect(ui->filterModePushButton, SIGNAL(clicked()), this, SLOT(buttonFilterModeClicked())); + + // filter + connect(ui->filterPushButton, SIGNAL(clicked()), this, SLOT(buttonFilterClicked())); + + // consolidate + connect(ui->consolidateButton, SIGNAL(clicked()), this, SLOT(buttonConsolidateClicked())); + + ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 150); + ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 170); + ui->treeWidget->setColumnWidth(COLUMN_LABEL, 200); ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290); ui->treeWidget->setColumnWidth(COLUMN_DATE, 110); ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100); ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100); - ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but dont show it - ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but dont show it - ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64_t in this column, but dont show it - ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64_t in this column, but dont show it + ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64_t in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64_t in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_CHANGE_BOOL, true); // store change flag but don't show it + + ui->filterModePushButton->setToolTip(tr("Flips the filter mode between selecting inputs less than or equal to the " + "provided value (<=) and greater than or equal to the provided value (>=). " + "The filter also automatically limits the number of inputs to %1, in " + "ascending order for <= and descending order for >=." + ).arg(m_inputSelectionLimit)); + + ui->consolidateSendReadyLabel->hide(); // default view is sorted by amount desc sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder); @@ -153,26 +175,222 @@ void CoinControlDialog::buttonBoxClicked(QAbstractButton* button) { if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) done(QDialog::Accepted); // closes the dialog + + if (m_consolidationAddress.second.size()) + { + SendCoinsRecipient consolidationRecipient; + + qint64 amount = 0; + bool parse_status = false; + + consolidationRecipient.label = m_consolidationAddress.first; + consolidationRecipient.address = m_consolidationAddress.second; + parse_status = BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), + ui->coinControlAfterFeeLabel->text() + .left(ui->coinControlAfterFeeLabel->text().indexOf(" ")), + &amount); + + if (parse_status) consolidationRecipient.amount = amount; + + emit selectedConsolidationRecipientSignal(consolidationRecipient); + } + + showHideConsolidationReadyToSend(); } // (un)select all void CoinControlDialog::buttonSelectAllClicked() { - Qt::CheckState state = Qt::Checked; + ui->treeWidget->setEnabled(false); for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != m_ToState) + ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, m_ToState); + ui->treeWidget->setEnabled(true); + + if (m_ToState == Qt::Checked) { - if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked) + m_ToState = Qt::Unchecked; + } + else + { + m_ToState = Qt::Checked; + } + + if (m_ToState == Qt::Checked) + { + ui->selectAllPushButton->setText("Select All"); + } + else + { + ui->selectAllPushButton->setText("Select None"); + } + + CoinControlDialog::updateLabels(model, this); + showHideConsolidationReadyToSend(); +} + +void CoinControlDialog::maxMinOutputValueChanged() +{ + + bool maxMinOutputValueValid = false; + + ui->maxMinOutputValue->value(&maxMinOutputValueValid); + + // If someone has put a value in the filter amount field, then consolidate should be disabled until the + // filter button is pressed to apply the filter. If the field is empty, then the consolidation can work + // without the filter application first, (i.e. consolidation is enabled), because the idea is to select + // up to the m_inputSelectionLimit number of inputs either from smallest upward or largest downward by + // following the <= or >= filter mode button. This shortcut is mainly for convenience. + if (maxMinOutputValueValid) + { + ui->consolidateButton->setEnabled(false); + } + else + { + ui->consolidateButton->setEnabled(true); + } + + showHideConsolidationReadyToSend(); +} + +void CoinControlDialog::buttonFilterModeClicked() +{ + if (m_FilterMode) + { + m_FilterMode = false; + ui->filterModePushButton->setText(">="); + } + else + { + m_FilterMode = true; + ui->filterModePushButton->setText("<="); + } +} + +void CoinControlDialog::buttonFilterClicked() +{ + // Don't limit the number of outputs for the filter only operation. + filterInputsByValue(m_FilterMode, ui->maxMinOutputValue->value(), std::numeric_limits::max()); + + ui->consolidateButton->setEnabled(true); + showHideConsolidationReadyToSend(); +} + +bool CoinControlDialog::filterInputsByValue(const bool& less, const CAmount& inputFilterValue, + const unsigned int& inputSelectionLimit) +{ + // Disable generating update signals unnecessarily during this filter operation. + ui->treeWidget->setEnabled(false); + + QTreeWidgetItemIterator iter(ui->treeWidget); + + // If less is true, then we are choosing the smallest inputs upward, and so the map comparator needs to be "less than". + // If less is false, then we are choosing the largest inputs downward, and so the map comparator needs to be "greater + // than". + auto comp = [less](CAmount a, CAmount b) + { + if (less) + { + return (a < b); + } + else { - state = Qt::Unchecked; - break; + return (a > b); } + }; + + std::multimap, decltype(comp)> input_map(comp); + + bool culled_inputs = false; + + while (*iter) + { + CAmount input_value = (*iter)->text(COLUMN_AMOUNT_INT64).toLongLong(); + COutPoint outpoint(uint256S((*iter)->text(COLUMN_TXHASH).toStdString()), (*iter)->text(COLUMN_VOUT_INDEX).toUInt()); + + if ((*iter)->checkState(COLUMN_CHECKBOX) == Qt::Checked) + { + if ((*iter)->text(COLUMN_TXHASH).length() == 64) + { + if ((less && input_value <= inputFilterValue) || (!less && input_value >= inputFilterValue)) + { + input_map.insert(std::make_pair(input_value, std::make_pair(*iter, outpoint))); + } + else + { + (*iter)->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + coinControl->UnSelect(outpoint); + } + } + } + + ++iter; } - ui->treeWidget->setEnabled(false); - for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) - if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state) - ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state); + + // The second loop is to limit the number of selected outputs to the inputCountLimit. + unsigned int input_count = 0; + + for (auto& input : input_map) + { + if (input_count >= inputSelectionLimit) + { + LogPrint(BCLog::LogFlags::MISC, "INFO: %s: Culled input %u with value %f.", + __func__, input_count, (double) input.first / COIN); + + if (coinControl->IsSelected(input.second.second.hash, input.second.second.n)) + { + input.second.first->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + culled_inputs = true; + coinControl->UnSelect(input.second.second); + } + } + + ++input_count; + } + + // Reenable update signals. ui->treeWidget->setEnabled(true); + CoinControlDialog::updateLabels(model, this); + + // If the number of inputs selected was limited, then true is returned. + return culled_inputs; +} + +void CoinControlDialog::buttonConsolidateClicked() +{ + ConsolidateUnspentDialog consolidateUnspentDialog(this, m_inputSelectionLimit); + + std::map addressList; + + bool culled_inputs = false; + + // Note that we are applying the filter here to limit the number of inputs only to ensure the m_inputSelectionLimit + // input maximum is not exceeded for the purpose of consolidation. + CAmount outputFilterValue = 0; + + outputFilterValue = m_FilterMode ? MAX_MONEY: 0; + + culled_inputs = filterInputsByValue(m_FilterMode, outputFilterValue, m_inputSelectionLimit); + + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) + { + QString label = ui->treeWidget->topLevelItem(i)->text(COLUMN_LABEL); + QString address = ui->treeWidget->topLevelItem(i)->text(COLUMN_ADDRESS); + QString change = ui->treeWidget-> topLevelItem(i)->text(COLUMN_CHANGE_BOOL); + + if (!change.toInt()) addressList[address] = label; + } + + if (!addressList.empty()) consolidateUnspentDialog.SetAddressList(addressList); + + if (culled_inputs) consolidateUnspentDialog.SetOutputWarningVisible(true); + + connect(&consolidateUnspentDialog, SIGNAL(selectedConsolidationAddressSignal(std::pair)), + this, SLOT(selectedConsolidationAddressSlot(std::pair))); + + consolidateUnspentDialog.exec(); } // context menu @@ -379,6 +597,8 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all CoinControlDialog::updateLabels(model, this); } + + showHideConsolidationReadyToSend(); } // helper function, return human readable label for priority number @@ -656,6 +876,7 @@ void CoinControlDialog::updateView() // tooltip from where the change comes from itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); itemOutput->setText(COLUMN_LABEL, tr("(change)")); + itemOutput->setText(COLUMN_CHANGE_BOOL, QString::number(1)); } else if (!treeMode) { @@ -740,3 +961,34 @@ void CoinControlDialog::updateView() sortView(sortColumn, sortOrder); ui->treeWidget->setEnabled(true); } + +void CoinControlDialog::selectedConsolidationAddressSlot(std::pair address) +{ + m_consolidationAddress = address; + showHideConsolidationReadyToSend(); +} + +void CoinControlDialog::showHideConsolidationReadyToSend() +{ + if (m_consolidationAddress.second.size() && coinControl->HasSelected() && ui->consolidateButton->isEnabled()) + { + // This is more expensive. Only do if it passes the first two conditions above. We want to check + // and make sure that the number of inputs is less than m_inputSelectionLimit for consolidation purposes. + std::vector selectionList; + + coinControl->ListSelected(selectionList); + + if (selectionList.size() <= m_inputSelectionLimit) + { + ui->consolidateSendReadyLabel->show(); + } + else + { + ui->consolidateSendReadyLabel->hide(); + } + } + else + { + ui->consolidateSendReadyLabel->hide(); + } +} diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index cacdb1cb00..b948181bef 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -1,6 +1,9 @@ #ifndef COINCONTROLDIALOG_H #define COINCONTROLDIALOG_H +#include "walletmodel.h" +#include "amount.h" + #include #include #include @@ -33,6 +36,15 @@ class CoinControlDialog : public QDialog static QList payAmounts; static CCoinControl *coinControl; + // This is based on what will guarantee a successful transaction. + const size_t m_inputSelectionLimit; + +signals: + void selectedConsolidationRecipientSignal(SendCoinsRecipient consolidationRecipient); + +public slots: + bool filterInputsByValue(const bool& less, const CAmount& inputFilterValue, const unsigned int& inputSelectionLimit); + private: Ui::CoinControlDialog *ui; WalletModel *model; @@ -45,9 +57,14 @@ class CoinControlDialog : public QDialog //QAction *lockAction; //QAction *unlockAction; + std::pair m_consolidationAddress; + Qt::CheckState m_ToState = Qt::Checked; + bool m_FilterMode = true; + QString strPad(QString, int, QString); void sortView(int, Qt::SortOrder); void updateView(); + void showHideConsolidationReadyToSend(); enum { @@ -61,7 +78,8 @@ class CoinControlDialog : public QDialog COLUMN_TXHASH, COLUMN_VOUT_INDEX, COLUMN_AMOUNT_INT64, - COLUMN_PRIORITY_INT64 + COLUMN_PRIORITY_INT64, + COLUMN_CHANGE_BOOL }; private slots: @@ -86,6 +104,11 @@ private slots: void headerSectionClicked(int); void buttonBoxClicked(QAbstractButton*); void buttonSelectAllClicked(); + void maxMinOutputValueChanged(); + void buttonFilterModeClicked(); + void buttonFilterClicked(); + void buttonConsolidateClicked(); + void selectedConsolidationAddressSlot(std::pair address); //void updateLabelLocked(); }; diff --git a/src/qt/consolidateunspentdialog.cpp b/src/qt/consolidateunspentdialog.cpp new file mode 100644 index 0000000000..74f78b8fe2 --- /dev/null +++ b/src/qt/consolidateunspentdialog.cpp @@ -0,0 +1,83 @@ +#include "consolidateunspentdialog.h" +#include "ui_consolidateunspentdialog.h" + +#include "util.h" + +using namespace std; + +ConsolidateUnspentDialog::ConsolidateUnspentDialog(QWidget *parent, size_t inputSelectionLimit) : + QDialog(parent), + ui(new Ui::ConsolidateUnspentDialog), + m_inputSelectionLimit(inputSelectionLimit) +{ + ui->setupUi(this); + + ui->addressTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + + // ok button + connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*))); + + // destination address selection + connect(ui->addressTableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(addressSelectionChanged())); + + ui->outputLimitWarningLabel->setText(tr("Note: The number of inputs selected for consolidation has been " + "limited to %1 to prevent a transaction failure due to too many " + "inputs.").arg(m_inputSelectionLimit)); + SetOutputWarningVisible(false); +} + +ConsolidateUnspentDialog::~ConsolidateUnspentDialog() +{ + delete ui; +} + +// --------------------------------------------------------- address - label +void ConsolidateUnspentDialog::SetAddressList(const std::map& addressList) +{ + ui->addressTableWidget->setSortingEnabled(false); + + int row = 0; + for (const auto& iter : addressList) + { + ui->addressTableWidget->insertRow(row); + + QTableWidgetItem* label = new QTableWidgetItem(iter.second); + QTableWidgetItem* address = new QTableWidgetItem(iter.first); + + if (label != nullptr) ui->addressTableWidget->setItem(row, 0, label); + if (address != nullptr) ui->addressTableWidget->setItem(row, 1, address); + + ++row; + } + + ui->addressTableWidget->setCurrentItem(ui->addressTableWidget->item(0, 1)); +} + +void ConsolidateUnspentDialog::SetOutputWarningVisible(bool status) +{ + ui->outputLimitWarningIconLabel->setVisible(status); + ui->outputLimitWarningLabel->setVisible(status); +} + +// ok button +void ConsolidateUnspentDialog::buttonBoxClicked(QAbstractButton* button) +{ + emit selectedConsolidationAddressSignal(m_selectedDestinationAddress); + + // closes the dialog + if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) done(QDialog::Accepted); +} + +void ConsolidateUnspentDialog::addressSelectionChanged() +{ + int currentRow = ui->addressTableWidget->currentRow(); + + QTableWidgetItem* selectedLabel = ui->addressTableWidget->item(currentRow, 0); + QTableWidgetItem* selectedAddress = ui->addressTableWidget->item(currentRow, 1); + + m_selectedDestinationAddress = std::make_pair(selectedLabel->text(), selectedAddress->text()); + + LogPrint(BCLog::LogFlags::MISC, "INFO: %s: Label %, Address %s selected.", __func__, + m_selectedDestinationAddress.first.toStdString(), + m_selectedDestinationAddress.second.toStdString()); +} diff --git a/src/qt/consolidateunspentdialog.h b/src/qt/consolidateunspentdialog.h new file mode 100644 index 0000000000..440e4f160b --- /dev/null +++ b/src/qt/consolidateunspentdialog.h @@ -0,0 +1,38 @@ +#ifndef CONSOLIDATEUNSPENTDIALOG_H +#define CONSOLIDATEUNSPENTDIALOG_H + +#include +#include +#include + +namespace Ui { + class ConsolidateUnspentDialog; +} + +class ConsolidateUnspentDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ConsolidateUnspentDialog(QWidget *parent = 0, size_t inputSelectionLimit = 600); + ~ConsolidateUnspentDialog(); + + void SetAddressList(const std::map& addressList); + void SetOutputWarningVisible(bool status); + +signals: + void selectedConsolidationAddressSignal(std::pair address); + +private: + Ui::ConsolidateUnspentDialog *ui; + + std::pair m_selectedDestinationAddress; + + size_t m_inputSelectionLimit; + +private slots: + void buttonBoxClicked(QAbstractButton *button); + void addressSelectionChanged(); +}; + +#endif // CONSOLIDATEUNSPENTDIALOG_H diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index 90c960abc2..fb5c4e92eb 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -6,8 +6,8 @@ 0 0 - 1000 - 500 + 1172 + 547 @@ -285,90 +285,149 @@ - - - - 0 - 40 - + + + 14 - - QFrame::StyledPanel - - - QFrame::Sunken - - - - - 10 - 0 - 781 - 41 - - - - - 14 - - - - - - 0 - 0 - - - - (un)select all - - - - - - - - 0 - 0 - - - - Tree &mode - - - true - - - - - - - - 0 - 0 - - - - &List mode - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + + 0 + 0 + + + + Toggles between selecting all and selecting none. + + + Select All + + + + + + + + 0 + 0 + + + + Tree &mode + + + true + + + + + + + + 0 + 0 + + + + &List mode + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Select inputs + + + + + + + + + + <= + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + Filters the already selected inputs. + + + Filter + + + + + + + Pushing this button after making a input selection either manually or with the filter will present a destination address list where you specify a single address as the destination for the consolidated output. The send (Pay To) entry will be filled in with this address and you can finish the consolidation by pressing the send button. + + + Consolidate + + + + + + + The consolidation transaction is ready to send to self. Please press the ok button to go to the send dialog. + + + Ready to consolidate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -471,6 +530,12 @@ QTreeWidget
coincontroltreewidget.h
+ + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+ 1 +
diff --git a/src/qt/forms/consolidateunspentdialog.ui b/src/qt/forms/consolidateunspentdialog.ui new file mode 100644 index 0000000000..18c50ac5be --- /dev/null +++ b/src/qt/forms/consolidateunspentdialog.ui @@ -0,0 +1,132 @@ + + + ConsolidateUnspentDialog + + + + 0 + 0 + 819 + 513 + + + + Consolidate Unspent Outputs (UTXOs) + + + + + 640 + 440 + 161 + 61 + + + + QDialogButtonBox::Ok + + + + + + 20 + 30 + 171 + 221 + + + + Select Destination Address for Consolidation + + + true + + + + + + 210 + 300 + 591 + 121 + + + + + + + true + + + + + + 210 + 20 + 591 + 241 + + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + 2 + + + 90 + + + true + + + false + + + + Label + + + + + Address + + + + + + + 50 + 310 + 111 + 101 + + + + + + + :/icons/warning + + + true + + + + + + + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 1a267c6b2e..2b5e1e8cfd 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -605,3 +605,9 @@ QStatusBar QToolTip { #capsLabel{ font-weight:bold; } + +/* ConsolidateUnspentDialog */ + +#consolidateSendReadyLabel{ + color: rgb(55, 250, 55); +} diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index fb4f9a4a60..3922f6e310 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -614,3 +614,9 @@ QStatusBar .QFrame QLabel { #capsLabel{ font-weight:bold; } + +/* ConsolidateUnspentDialog */ + +#consolidateSendReadyLabel{ + color: rgb(0, 128, 0); +} diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 648f0a8ef6..09c742b2b0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -2,7 +2,6 @@ #include "ui_sendcoinsdialog.h" #include "init.h" -#include "walletmodel.h" #include "addresstablemodel.h" #include "addressbookpage.h" @@ -432,6 +431,10 @@ void SendCoinsDialog::coinControlButtonClicked() { CoinControlDialog dlg; dlg.setModel(model); + + connect(&dlg, SIGNAL(selectedConsolidationRecipientSignal(SendCoinsRecipient)), + this, SLOT(selectedConsolidationRecipient(SendCoinsRecipient))); + dlg.exec(); coinControlUpdateLabels(); } @@ -442,6 +445,24 @@ void SendCoinsDialog::coinControlResetButtonClicked() coinControlUpdateLabels(); } +void SendCoinsDialog::selectedConsolidationRecipient(SendCoinsRecipient consolidationRecipient) +{ + ui->coinControlChangeCheckBox->setChecked(true); + ui->coinControlChangeEdit->setText(consolidationRecipient.address); + + for (int i = ui->entries->count() - 1; i >= 0; --i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + + if (entry) + { + removeEntry(entry); + } + } + + pasteEntry(consolidationRecipient); +} + // Coin Control: checkbox custom change address void SendCoinsDialog::coinControlChangeChecked(int state) { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index f6bf390225..81878ca005 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -4,12 +4,12 @@ #include #include +#include "walletmodel.h" + namespace Ui { class SendCoinsDialog; } -class WalletModel; class SendCoinsEntry; -class SendCoinsRecipient; QT_BEGIN_NAMESPACE class QUrl; @@ -64,6 +64,7 @@ private slots: void coinControlClipboardPriority(); void coinControlClipboardLowOutput(); void coinControlClipboardChange(); + void selectedConsolidationRecipient(SendCoinsRecipient consolidationRecipient); void updateIcons(); }; From 385b8effd204dbd75d12016ff860499e92a38eb8 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 1 May 2021 13:57:12 -0400 Subject: [PATCH 077/280] Implement GetMaxInputsForConsolidationTxn() This implements a chain parameter function to get the maximum number of inputs allowable for a UTXO consolidation transaction with either the RPC consolidateunspent or the GUI "consolidate" button in coin control. The default value for the RPC consolidateunspent function has been changed to be the same as the upper clamp at the value returned by GetMaxInputsForConsolidationTxn(). The help returned for consolidateunspent uses that value as well. --- src/chainparams.h | 5 --- src/miner.cpp | 1 + src/policy/policy.cpp | 11 +++++ src/policy/policy.h | 15 +++++++ src/qt/coincontroldialog.cpp | 3 +- src/rpc/rawtransaction.cpp | 79 +++++++++++++++++++----------------- 6 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/chainparams.h b/src/chainparams.h index 926749d38a..a13521ef57 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -145,8 +145,3 @@ inline int GetNewbieSnapshotFixHeight() { return fTestNet ? 1480000 : 2197000; } - -inline unsigned int GetMinimumConnectionsRequiredForStaking() -{ - return fTestNet ? 1 : 3; -} diff --git a/src/miner.cpp b/src/miner.cpp index 2691c82521..16debdb146 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -19,6 +19,7 @@ #include "gridcoin/staking/reward.h" #include "gridcoin/staking/status.h" #include "gridcoin/tally.h" +#include "policy/policy.h" #include "policy/fees.h" #include "util.h" #include "validation.h" diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 937ed0b5b9..f3ef0ff20e 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -175,3 +175,14 @@ bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs) return true; } + +unsigned int GetMinimumConnectionsRequiredForStaking() +{ + return fTestNet ? 1 : 3; +} + +unsigned int GetMaxInputsForConsolidationTxn() +{ + return (unsigned int) 600; +} + diff --git a/src/policy/policy.h b/src/policy/policy.h index df7efc138e..dc0e781a63 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -23,4 +23,19 @@ bool IsStandardTx(const CTransaction& tx); */ bool AreInputsStandard(const CTransaction& tx, const MapPrevTx& mapInputs); +//! +//! \brief Gets the minimum number of connections required for a wallet to stake. +//! +//! \return unsigned int minimum number of connections to stake +//! +unsigned int GetMinimumConnectionsRequiredForStaking(); + +//! +//! \brief Gets the maximum number of inputs supported for a UTXO consolidation transaction to ensure +//! the transaction does not exceed the maximum size and fail as a result. +//! +//! \return unsigned int maximum number of consolidation inputs. +//! +unsigned int GetMaxInputsForConsolidationTxn(); + #endif // BITCOIN_POLICY_POLICY_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 71e4429484..dd605f2104 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -5,6 +5,7 @@ #include "bitcoinunits.h" #include "addresstablemodel.h" #include "optionsmodel.h" +#include "policy/policy.h" #include "policy/fees.h" #include "validation.h" #include "wallet/coincontrol.h" @@ -29,7 +30,7 @@ CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); CoinControlDialog::CoinControlDialog(QWidget *parent) : QDialog(parent), - m_inputSelectionLimit(600), + m_inputSelectionLimit(GetMaxInputsForConsolidationTxn()), ui(new Ui::CoinControlDialog), model(0) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5ef4f1f4fb..0d727c7023 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -14,6 +14,7 @@ #include "gridcoin/support/block_finder.h" #include "gridcoin/tx_message.h" #include "gridcoin/voting/payloads.h" +#include "policy/policy.h" #include "policy/fees.h" #include "primitives/transaction.h" #include "protocol.h" @@ -493,38 +494,43 @@ UniValue listunspent(const UniValue& params, bool fHelp) UniValue consolidateunspent(const UniValue& params, bool fHelp) { + std::stringstream error_strm; + + error_strm << "consolidateunspent
[UTXO size] [maximum number of inputs] [sweep all addresses] [sweep change]\n" + "\n" + "
: The Gridcoin address target for consolidation.\n" + "\n" + "[UTXO size]: Optional parameter for target consolidation output size.\n" + "\n" + "[maximum number of inputs]: Defaults and clamped to " + << std::to_string(GetMaxInputsForConsolidationTxn()) + << " maximum to prevent transaction failures.\n" + "\n" + "[sweep all addresses]: Boolean to indicate whether all addresses should be used for inputs to the\n" + " consolidation. If true, the source of the consolidation is all addresses and\n" + " the output will be to the specified address, otherwise inputs will only be\n" + " sourced from the same address.\n" + "\n" + "[sweep change]: Boolean to indicate whether change associated with the address should be\n" + " consolidated. If [sweep all addresses] is true then this is also forced true.\n" + "\n" + "consolidateunspent performs a single transaction to consolidate UTXOs to/on a given address. The optional\n" + "parameter of UTXO size will result in consolidating UTXOs to generate the largest output possible less\n" + "than that size or the total value of the specified maximum number of smallest inputs, whichever is less.\n" + "\n" + "The script is designed to be run repeatedly and will become a no-op if the UTXO's are consolidated such\n" + "that no more meet the specified criteria. This is ideal for automated periodic scripting.\n" + "\n" + "To consolidate the entire wallet to one address do something like:\n" + "\n" + "consolidateunspent
200 true repeatedly until there are\n" + "no more UTXOs to consolidate.\n" + "\n" + "In all cases the address MUST exist in your wallet beforehand. If doing this for the purpose of creating\n" + "a new smaller wallet, create a new address beforehand to serve as the target of the consolidation.\n"; + if (fHelp || params.size() < 1 || params.size() > 5) - throw runtime_error( - "consolidateunspent
[UTXO size] [maximum number of inputs] [sweep all addresses] [sweep change]\n" - "\n" - "
: The Gridcoin address target for consolidation.\n" - "\n" - "[UTXO size]: Optional parameter for target consolidation output size.\n" - "\n" - "[maximum number of inputs]: Defaults to 50, clamped to 200 maximum to prevent transaction failures.\n" - "\n" - "[sweep all addresses]: Boolean to indicate whether all addresses should be used for inputs to the\n" - " consolidation. If true, the source of the consolidation is all addresses and\n" - " the output will be to the specified address, otherwise inputs will only be\n" - " sourced from the same address.\n" - "\n" - "[sweep change]: Boolean to indicate whether change associated with the address should be\n" - " consolidated. If [sweep all addresses] is true then this is also forced true.\n" - "\n" - "consolidateunspent performs a single transaction to consolidate UTXOs to/on a given address. The optional\n" - "parameter of UTXO size will result in consolidating UTXOs to generate the largest output possible less\n" - "than that size or the total value of the specified maximum number of smallest inputs, whichever is less.\n" - "\n" - "The script is designed to be run repeatedly and will become a no-op if the UTXO's are consolidated such\n" - "that no more meet the specified criteria. This is ideal for automated periodic scripting.\n" - "\n" - "To consolidate the entire wallet to one address do something like:\n" - "\n" - "consolidateunspent
200 true repeatedly until there are\n" - "no more UTXOs to consolidate.\n" - "\n" - "In all cases the address MUST exist in your wallet beforehand. If doing this for the purpose of creating\n" - "a new smaller wallet, create a new address beforehand to serve as the target of the consolidation.\n"); + throw runtime_error(error_strm.str()); UniValue result(UniValue::VOBJ); @@ -532,11 +538,7 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) CBitcoinAddress OptimizeAddress(sAddress); int64_t nConsolidateLimit = 0; - // Set default maximum consolidation to 50 inputs if it is not specified. This is based - // on performance tests on the Pi to ensure the transaction returns within a reasonable time. - // The performance tests on the Pi show about 3 UTXOs/second. Intel machines should do - // about 3x that. The GUI will not be responsive during the transaction due to locking. - unsigned int nInputNumberLimit = 50; + unsigned int nInputNumberLimit = GetMaxInputsForConsolidationTxn(); bool sweep_all_addresses = false; @@ -551,8 +553,9 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) if (params.size() > 4 && !sweep_all_addresses) sweep_change = params[4].get_bool(); - // Clamp InputNumberLimit to 200. Above 200 risks an invalid transaction due to the size. - nInputNumberLimit = std::min(nInputNumberLimit, 200); + // Clamp InputNumberLimit to GetMaxInputsForConsolidationTxn(). Above that number of inputs risks an invalid transaction + // due to the size. + nInputNumberLimit = std::min(nInputNumberLimit, GetMaxInputsForConsolidationTxn()); if (!OptimizeAddress.IsValid()) { From 195ccb97e73b7043a2b00a89dfe5b9b07456ed54 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 25 Apr 2021 01:11:04 -0500 Subject: [PATCH 078/280] Change Windows/Linux GUI menu bar to a menu button This removes the standard menu bar from the main application layout in favor of a menu button placed at the top-left corner of the window. It enables the visual style of the proposed GUI designs which do not have a menu bar. The menu button does not appear on macOS because the macOS standard menu bar at the top of the screen already separates the menus from the main layout. --- src/Makefile.qt.include | 2 + src/qt/bitcoin.qrc | 2 + src/qt/bitcoingui.cpp | 43 ++++++++++++++------- src/qt/bitcoingui.h | 6 +++ src/qt/res/icons/menu.svg | 1 + src/qt/res/icons/menu_active.svg | 1 + src/qt/res/stylesheets/dark_stylesheet.qss | 29 +++++++++++++- src/qt/res/stylesheets/light_stylesheet.qss | 31 +++++++++++++-- 8 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 src/qt/res/icons/menu.svg create mode 100644 src/qt/res/icons/menu_active.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index a9a3b824c3..79d9666ff0 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -316,6 +316,8 @@ RES_ICONS = \ qt/res/icons/gridcoin.ico \ qt/res/icons/gridcoin_testnet.ico \ qt/res/icons/key.png \ + qt/res/icons/menu.svg \ + qt/res/icons/menu_active.svg \ qt/res/icons/message.svg \ qt/res/icons/qrcode.png \ qt/res/icons/quit.png \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 930add0204..bbaa6d47cc 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -162,6 +162,8 @@ res/icons/tx_contract_beacon.svg res/icons/tx_contract_voting.svg res/icons/message.svg + res/icons/menu.svg + res/icons/menu_active.svg res/images/splash3.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index aa444097a0..c1f54b4d0b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -206,11 +206,6 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): signVerifyMessageDialog = new SignVerifyMessageDialog(this); - QVBoxLayout *centralVbox = new QVBoxLayout(this); - centralVbox->setContentsMargins(0, 0, 0, 0); - centralVbox->setSpacing(0); - centralVbox->addWidget(appMenuBar); - centralWidget = new QStackedWidget(this); centralWidget->addWidget(overviewPage); centralWidget->addWidget(transactionsPage); @@ -218,11 +213,8 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(sendCoinsPage); centralWidget->addWidget(votingPage); - centralVbox->addWidget(centralWidget); - QWidget* centralWidgetWrapper = new QWidget(this); - centralWidgetWrapper->setLayout(centralVbox); - setCentralWidget(centralWidgetWrapper); + setCentralWidget(centralWidget); // Create status bar statusBar(); @@ -532,13 +524,15 @@ void BitcoinGUI::createMenuBar() #ifdef Q_OS_MAC // Create a decoupled menu bar on Mac which stays even if the window is closed appMenuBar = new QMenuBar(); + QMenu *file = appMenuBar->addMenu(tr("&File")); #else - // Get the main window's menu bar on other platforms - appMenuBar = menuBar(); + // Windows and Linux: collapse the main application's menu bar into a menu + // button. On macOS, we'll continue to use the system's separate menu bar. + appMenuBar = new QMenu(); + QMenu *file = appMenuBar; #endif // Configure the menus - QMenu *file = appMenuBar->addMenu(tr("&File")); file->addAction(backupWalletAction); file->addAction(exportAction); file->addAction(signMessageAction); @@ -553,7 +547,9 @@ void BitcoinGUI::createMenuBar() file->addAction(resetblockchainAction); file->addSeparator(); +#ifdef Q_OS_MAC file->addAction(quitAction); +#endif QMenu *settings = appMenuBar->addMenu(tr("&Settings")); settings->addAction(encryptWalletAction); @@ -581,6 +577,11 @@ void BitcoinGUI::createMenuBar() help->addAction(diagnosticsAction); help->addSeparator(); help->addAction(aboutAction); + +#ifndef Q_OS_MAC + file->addSeparator(); + file->addAction(quitAction); +#endif } void BitcoinGUI::createToolBars() @@ -589,12 +590,26 @@ void BitcoinGUI::createToolBars() logoLabel->setObjectName("toolbarLogoLabel"); connect(logoLabel, SIGNAL(clicked()), this, SLOT(websiteClicked())); + QWidget *logoWrapper = new QWidget(); + logoWrapper->setObjectName("toolbarLogoWrapper"); + logoWrapper->setLayout(new QVBoxLayout()); + logoWrapper->layout()->addWidget(logoLabel); + +#ifndef Q_OS_MAC + // Windows and Linux: collapse the main application's menu bar into a menu + // button. On macOS, we'll continue to use the system's separate menu bar. + QPushButton *menuButton = new QPushButton(logoWrapper); + menuButton->setObjectName("toolbarMenuButton"); + menuButton->resize(GRC::ScaleSize(this, 24)); + menuButton->setMenu(appMenuBar); +#endif + QWidget *boincLabelSpacer = new QWidget(); boincLabelSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ClickLabel *boincLabel = new ClickLabel(); boincLabel->setObjectName("toolbarBoincLabel"); - connect(logoLabel, SIGNAL(clicked()), this, SLOT(boincClicked())); + connect(boincLabel, SIGNAL(clicked()), this, SLOT(boincClicked())); // "Tabs" toolbar (vertical, aligned on left side of overview screen). QToolBar *toolbar = addToolBar("Tabs toolbar"); @@ -607,7 +622,7 @@ void BitcoinGUI::createToolBars() // Setting a taller height than the rendered icon provides additional // padding between the icon and the button text: toolbar->setIconSize(GRC::ScaleSize(this, 16, 24)); - toolbar->addWidget(logoLabel); + toolbar->addWidget(logoWrapper); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index cb181eb2ab..3b1776642a 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -93,7 +93,13 @@ class BitcoinGUI : public QMainWindow QLabel *labelScraperIcon; QLabel *labelBeaconIcon; + // Windows and Linux: collapse the main application's menu bar into a menu + // button. On macOS, we'll continue to use the system's separate menu bar. +#ifdef Q_OS_MAC QMenuBar *appMenuBar; +#else + QMenu *appMenuBar; +#endif QAction *overviewAction; QAction *historyAction; QAction *quitAction; diff --git a/src/qt/res/icons/menu.svg b/src/qt/res/icons/menu.svg new file mode 100644 index 0000000000..f8ec2f0faf --- /dev/null +++ b/src/qt/res/icons/menu.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/menu_active.svg b/src/qt/res/icons/menu_active.svg new file mode 100644 index 0000000000..020805d952 --- /dev/null +++ b/src/qt/res/icons/menu_active.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 2b5e1e8cfd..1e63f5c9ff 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -429,6 +429,7 @@ QTabBar::tab:!selected:hover { QToolBar#toolbar { border-right: 0.065em solid rgb(18, 26, 34); height: 100%; + padding: 0; } QToolBar#toolbar QToolButton { @@ -452,12 +453,36 @@ QToolBar#toolbar::separator { margin: 0.25em 1.5em; } +#toolbarLogoWrapper { + min-height: 4em; + max-height: 4em; + border-bottom: 0.065em solid rgb(18, 26, 34); +} + +#toolbarMenuButton { + image: url(:/icons/menu); + background: none; + border: none; + border-radius: 0; + padding: 0; +} + +#toolbarMenuButton:hover, +#toolbarMenuButton:pressed { + image: url(:/icons/menu_active); + background-color: rgb(26, 145, 235); +} + +#toolbarMenuButton::menu-indicator { + width: 0; + height: 0; +} + #toolbarLogoLabel { image: url(:/images/gridcoin); - border-bottom: 0.065em solid rgb(18, 26, 34); min-width: 2.75em; min-height: 2.75em; - padding: 1em 1.5em; + max-height: 2.75em; } #toolbarBoincLabel { diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 3922f6e310..014e112e4a 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -440,6 +440,7 @@ QToolBar#toolbar { background-color: rgb(54, 1, 102); border: none; height: 100%; + padding: 0; } QToolBar#toolbar QToolButton { @@ -464,13 +465,37 @@ QToolBar#toolbar::separator { margin: 0.25em 1.5em; } -#toolbarLogoLabel { - image: url(:/images/gridcoin); +#toolbarLogoWrapper { background-color: rgb(48, 11, 82); + min-height: 4em; + max-height: 4em; border-bottom: 0.065em solid rgb(74, 26, 117); +} + +#toolbarMenuButton { + image: url(:/icons/menu); + background: none; + border: none; + border-radius: 0; + padding: 0; +} + +#toolbarMenuButton:hover, +#toolbarMenuButton:pressed { + image: url(:/icons/menu_active); + background: rgb(109, 25, 186); +} + +#toolbarMenuButton::menu-indicator { + width: 0; + height: 0; +} + +#toolbarLogoLabel { + image: url(:/images/gridcoin); min-width: 2.75em; min-height: 2.75em; - padding: 1em 1.5em; + max-height: 2.75em; } #toolbarBoincLabel { From 0d335554fc628cc97ff9c231d66504eb8302ec7c Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 25 Apr 2021 01:11:08 -0500 Subject: [PATCH 079/280] Move GUI overview page error messages to status bar This replaces the "error messages" field on the overview page with a section in the status bar to match the proposed GUI design. --- src/main.cpp | 15 ++++++------ src/qt/bitcoingui.cpp | 17 ++++++++++++- src/qt/bitcoingui.h | 1 + src/qt/forms/overviewpage.ui | 23 ------------------ src/qt/overviewpage.cpp | 47 +++++++++++++----------------------- src/qt/overviewpage.h | 1 - 6 files changed, 42 insertions(+), 62 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a37f4d9338..3926025b36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -191,13 +191,6 @@ void GlobalStatus::SetGlobalStatus(bool force) errors.clear(); - std::string Alerts = GetWarnings("statusbar"); - - if (!Alerts.empty()) - { - errors += _("Alert: ") + Alerts + "; "; - } - if (difficulty < 0.1) { errors += _("Low difficulty!; "); @@ -3057,6 +3050,14 @@ string GetWarnings(string strFor) } } + const GlobalStatus::globalStatusType status = g_GlobalStatus.GetGlobalStatus(); + + if (!strStatusBar.empty() && !status.errors.empty()) { + strStatusBar += "; "; + } + + strStatusBar += status.errors; + if (strFor == "statusbar") return strStatusBar; assert(!"GetWarnings() : invalid parameter"); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c1f54b4d0b..8ca96f813d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -650,6 +650,10 @@ void BitcoinGUI::createToolBars() statusBar()->addWidget(testnetLabel); } + statusbarAlertsLabel = new QLabel(); + statusBar()->addWidget(statusbarAlertsLabel); + statusBar()->layout()->setAlignment(statusbarAlertsLabel, Qt::AlignLeft | Qt::AlignVCenter); + frameBlocks->setContentsMargins(0,0,0,0); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); @@ -938,7 +942,6 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) return; } - QString strStatusBarWarnings = clientModel->getStatusBarWarnings(); QString tooltip(tr("Processed %n block(s) of transaction history.", "", count)); QDateTime lastBlockDate = clientModel->getLastBlockDate(); @@ -1484,6 +1487,18 @@ void BitcoinGUI::updateGlobalStatus() { overviewPage->updateGlobalStatus(); setNumConnections(clientModel->getNumConnections()); + + QString warnings = clientModel->getStatusBarWarnings(); + + if (!warnings.isEmpty()) + { + statusbarAlertsLabel->setText(warnings); + statusbarAlertsLabel->setVisible(true); + } + else + { + statusbarAlertsLabel->setVisible(false); + } } catch(std::runtime_error &e) { diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 3b1776642a..5c865286a2 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -86,6 +86,7 @@ class BitcoinGUI : public QMainWindow SignVerifyMessageDialog *signVerifyMessageDialog; std::unique_ptr updateMessageDialog; + QLabel *statusbarAlertsLabel; QLabel *labelEncryptionIcon; QLabel *labelStakingIcon; QLabel *labelConnectionsIcon; diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 8176986061..d86bec3be5 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -852,29 +852,6 @@ - - - - Error Messages: - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index e0261a8294..114a89b516 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -155,8 +155,6 @@ OverviewPage::OverviewPage(QWidget *parent) : void OverviewPage::handleTransactionClicked(const QModelIndex &index) { - OverviewPage::UpdateBoincUtilization(); - if(filter) emit transactionClicked(filter->mapToSource(index)); } @@ -251,30 +249,6 @@ void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBa bool showImmature = immatureBalance != 0; ui->immatureLabel->setVisible(showImmature); ui->immatureTextLabel->setVisible(showImmature); - OverviewPage::UpdateBoincUtilization(); - -} - -void OverviewPage::UpdateBoincUtilization() -{ - { - LogPrint(BCLog::MISC, "OverviewPage::UpdateBoincUtilization()"); - - if (miner_first_pass_complete) g_GlobalStatus.SetGlobalStatus(true); - - const GlobalStatus::globalStatusStringType& globalStatusStrings = g_GlobalStatus.GetGlobalStatusStrings(); - - ui->blocksLabel->setText(QString::fromUtf8(globalStatusStrings.blocks.c_str())); - ui->difficultyLabel->setText(QString::fromUtf8(globalStatusStrings.difficulty.c_str())); - ui->netWeightLabel->setText(QString::fromUtf8(globalStatusStrings.netWeight.c_str())); - ui->coinWeightLabel->setText(QString::fromUtf8(globalStatusStrings.coinWeight.c_str())); - ui->errorsLabel->setText(QString::fromUtf8(globalStatusStrings.errors.c_str())); - } - - // GetCurrentPollTitle() locks cs_main: - ui->pollLabel->setText(QString::fromStdString(GRC::GetCurrentPollTitle()) - .left(80) - .replace(QChar('_'), QChar(' '), Qt::CaseSensitive)); } void OverviewPage::setResearcherModel(ResearcherModel *researcherModel) @@ -317,8 +291,6 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model->getOptionsModel(), SIGNAL(LimitTxnDisplayChanged(bool)), this, SLOT(updateTransactions())); connect(model, SIGNAL(transactionUpdated()), this, SLOT(updateTransactions())); - - UpdateBoincUtilization(); } // update the display unit, to not use the default ("BTC") @@ -400,10 +372,25 @@ void OverviewPage::showOutOfSyncWarning(bool fShow) { ui->walletStatusLabel->setVisible(fShow); ui->transactionsStatusLabel->setVisible(fShow); - OverviewPage::UpdateBoincUtilization(); } void OverviewPage::updateGlobalStatus() { - OverviewPage::UpdateBoincUtilization(); + { + LogPrint(BCLog::MISC, "OverviewPage::UpdateBoincUtilization()"); + + if (miner_first_pass_complete) g_GlobalStatus.SetGlobalStatus(true); + + const GlobalStatus::globalStatusStringType& globalStatusStrings = g_GlobalStatus.GetGlobalStatusStrings(); + + ui->blocksLabel->setText(QString::fromUtf8(globalStatusStrings.blocks.c_str())); + ui->difficultyLabel->setText(QString::fromUtf8(globalStatusStrings.difficulty.c_str())); + ui->netWeightLabel->setText(QString::fromUtf8(globalStatusStrings.netWeight.c_str())); + ui->coinWeightLabel->setText(QString::fromUtf8(globalStatusStrings.coinWeight.c_str())); + } + + // GetCurrentPollTitle() locks cs_main: + ui->pollLabel->setText(QString::fromStdString(GRC::GetCurrentPollTitle()) + .left(80) + .replace(QChar('_'), QChar(' '), Qt::CaseSensitive)); } diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index ab79c1c052..01c67db5d1 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -29,7 +29,6 @@ class OverviewPage : public QWidget void setWalletModel(WalletModel *model); void showOutOfSyncWarning(bool fShow); void updateGlobalStatus(); - void UpdateBoincUtilization(); public slots: void setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance); From 12b6dac5b2d1708b37e673742fbb22757f871ca2 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 25 Apr 2021 01:11:13 -0500 Subject: [PATCH 080/280] Refresh GUI overview page styles from redesign This updates the overview page to match the appearance from the proposed GUI design. - Add a header bar with status, magnitude, and available balance - Split content sections into cards - Change out of sync and researcher action needed labels to badges - Change beacon button to an icon - Limit the max content width for large window sizes - Update colors, layout, sizing, and spacing --- src/Makefile.qt.include | 7 + src/qt/bitcoin.qrc | 7 + src/qt/bitcoinunits.cpp | 21 + src/qt/bitcoinunits.h | 2 + src/qt/forms/overviewpage.ui | 1825 +++++++++-------- src/qt/overviewpage.cpp | 112 +- .../icons_dark/settings_action_needed.svg | 8 + .../icons_dark/sidebar_settings_active.svg | 1 + .../icons_dark/sidebar_settings_inactive.svg | 1 + src/qt/res/icons/icons_light/settings.svg | 1 + .../icons_light/settings_action_needed.svg | 8 + .../icons_light/sidebar_settings_active.svg | 1 + .../icons_light/sidebar_settings_inactive.svg | 1 + src/qt/res/icons/tx_input.svg | 2 +- src/qt/res/icons/tx_output.svg | 2 +- src/qt/res/stylesheets/dark_stylesheet.qss | 105 +- src/qt/res/stylesheets/light_stylesheet.qss | 86 +- 17 files changed, 1338 insertions(+), 852 deletions(-) create mode 100644 src/qt/res/icons/icons_dark/settings_action_needed.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_settings_active.svg create mode 100644 src/qt/res/icons/icons_dark/sidebar_settings_inactive.svg create mode 100644 src/qt/res/icons/icons_light/settings.svg create mode 100644 src/qt/res/icons/icons_light/settings_action_needed.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_settings_active.svg create mode 100644 src/qt/res/icons/icons_light/sidebar_settings_inactive.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 79d9666ff0..9c0cab65e7 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -342,6 +342,8 @@ RES_ICONS = \ qt/res/icons/warning.svg \ qt/res/icons/white_and_red_x.svg \ qt/res/icons/www.png \ + qt/res/icons/icons_light/settings.svg \ + qt/res/icons/icons_light/settings_action_needed.svg \ qt/res/icons/icons_light/sidebar_favorites_active.svg \ qt/res/icons/icons_light/sidebar_favorites_inactive.svg \ qt/res/icons/icons_light/sidebar_history_active.svg \ @@ -354,6 +356,8 @@ RES_ICONS = \ qt/res/icons/icons_light/sidebar_receive_inactive.svg \ qt/res/icons/icons_light/sidebar_send_active.svg \ qt/res/icons/icons_light/sidebar_send_inactive.svg \ + qt/res/icons/icons_light/sidebar_settings_active.svg \ + qt/res/icons/icons_light/sidebar_settings_inactive.svg \ qt/res/icons/icons_light/sidebar_unlocked_active.svg \ qt/res/icons/icons_light/sidebar_unlocked_inactive.svg \ qt/res/icons/icons_light/sidebar_voting_active.svg \ @@ -380,6 +384,7 @@ RES_ICONS = \ qt/res/icons/icons_light/status_sync_done.svg \ qt/res/icons/icons_light/status_sync_stalled.svg \ qt/res/icons/icons_light/status_sync_syncing.svg \ + qt/res/icons/icons_dark/settings_action_needed.svg \ qt/res/icons/icons_dark/sidebar_favorites_active.svg \ qt/res/icons/icons_dark/sidebar_favorites_inactive.svg \ qt/res/icons/icons_dark/sidebar_history_active.svg \ @@ -392,6 +397,8 @@ RES_ICONS = \ qt/res/icons/icons_dark/sidebar_receive_inactive.svg \ qt/res/icons/icons_dark/sidebar_send_active.svg \ qt/res/icons/icons_dark/sidebar_send_inactive.svg \ + qt/res/icons/icons_dark/sidebar_settings_active.svg \ + qt/res/icons/icons_dark/sidebar_settings_inactive.svg \ qt/res/icons/icons_dark/sidebar_unlocked_active.svg \ qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg \ qt/res/icons/icons_dark/sidebar_voting_active.svg \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index bbaa6d47cc..d21a98ff37 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -48,6 +48,8 @@ res/icons/icons_light/sidebar_receive_active.svg res/icons/icons_light/sidebar_send_inactive.svg res/icons/icons_light/sidebar_send_active.svg + res/icons/icons_light/sidebar_settings_inactive.svg + res/icons/icons_light/sidebar_settings_active.svg res/icons/icons_light/sidebar_voting_inactive.svg res/icons/icons_light/sidebar_voting_active.svg @@ -88,6 +90,8 @@ res/icons/icons_light/chevron_down.svg res/icons/icons_light/chevron_up.svg + res/icons/icons_light/settings.svg + res/icons/icons_light/settings_action_needed.svg res/icons/icons_dark/chevron_down.svg res/icons/icons_dark/chevron_up.svg + res/icons/icons_dark/settings_action_needed.svg res/icons/tx_pos_ss.svg res/icons/tx_por.svg diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 1bb2c2a2dc..afe2581106 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -117,6 +117,27 @@ QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) return format(unit, amount, plussign) + QString(" ") + name(unit); } +QString BitcoinUnits::formatOverviewRounded(qint64 amount) +{ + if (amount < factor(BTC)) { + return format(BTC, amount); + } + + qint64 round_scale = 10; + qint64 amount_temp = amount / factor(BTC); + + while (amount_temp /= 10) { + round_scale *= 10; + } + + round_scale = std::min(round_scale, factor(BTC) / 100); + + // Rounds half-down to avoid over-representing the amount: + const qint64 rounded_amount = static_cast(amount) / round_scale; + + return format(BTC, rounded_amount * round_scale); +} + bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { if(!valid(unit) || value.isEmpty()) diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 9b7c9e160e..70e4558092 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -44,6 +44,8 @@ class BitcoinUnits: public QAbstractListModel static QString format(int unit, qint64 amount, bool plussign=false); //! Format as string (with unit) static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); + //! Format as a rounded string approximation for overview presentation + static QString formatOverviewRounded(qint64 amount); //! Parse string to coin amount static bool parse(int unit, const QString &value, qint64 *val_out); ///@} diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index d86bec3be5..b0192885df 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -31,826 +31,1041 @@ Form - - - - - - - 6 - - - - - - 0 - 0 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 4 - - - 0 - 0 - + + 0 - - - 16777215 - 16777215 - + + 0 - - - - - 7 - - - - - Wallet - - - Qt::PlainText - - - - - - - The displayed information may be out of date. Your wallet automatically synchronizes with the Gridcoin network after a connection is established, but this process has not completed yet. - - - (out of sync) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 12 - - - - - Available: - - - true - - - - - - - - 0 - 0 - - - - IBeamCursor - - - Your current spendable balance - - - 0 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Immature Stake: - - - true - - - - - - - - 0 - 0 - - - - IBeamCursor - - - Amount staked for a recent block that must wait for 110 confirmations to mature before you can spend it. - - - 0 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Unconfirmed: - - - true - - - - - - - - 0 - 0 - - - - IBeamCursor - - - Total of transactions that have yet to be confirmed, and do not yet count toward the current balance - - - 0 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Immature: - - - true - - - - - - - - 0 - 0 - - - - Total mined coins that have not yet matured. - - - 0 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Total: - - - true - - - - - - - - 0 - 0 - - - - Your current total balance - - - 0 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - 7 - - - - - Staking - - - Qt::PlainText - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 12 - - - - - Blocks: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Difficulty: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Net Weight: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Coin Weight: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - 7 - - - - - Researcher - - - Qt::PlainText - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 12 - - - - - Status: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - CPID: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Magnitude: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Pending Reward: - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Open the researcher/beacon configuration wizard. - - - &Beacon... - - - false - - - false - - - - - - - - 0 - 0 - - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - 16 - 16 - - - - - - - :/icons/warning - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - Qt::NoTextInteraction - - - - - - - Action Needed - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 10000 - - - - - - - - - - - - - 6 - - - - - - 0 - 0 - + + 0 - - - 0 - 0 - + + 0 - - - 0 - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Recent transactions - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - The displayed information may be out of date. Your wallet automatically synchronizes with the Gridcoin network after a connection is established, but this process has not completed yet. - - - (out of sync) - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - - - - 1 - 0 - - - - - 0 - 0 - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractItemView::NoSelection - - - - - - - - - + + + + Account Overview + + + + + + + + + + 0 + 0 + + + + CPID + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0.00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Magnitude + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0.00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Available (GRC) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + - + - 12 + 0 - - - - Current Poll: + + + + Qt::Horizontal - + + + 0 + 20 + + + - - + + - - 0 + + 1 0 - - + + + 9 + + + 9 + + + 9 + + + + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 7 + + + + + Wallet + + + Qt::PlainText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Gridcoin network after a connection is established, but this process has not completed yet. + + + out of sync + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + 12 + + + + + Total: + + + true + + + + + + + + 0 + 0 + + + + IBeamCursor + + + Your current spendable balance + + + 0 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + IBeamCursor + + + Total of transactions that have yet to be confirmed, and do not yet count toward the current balance + + + 0 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + IBeamCursor + + + Amount staked for a recent block that must wait for 110 confirmations to mature before you can spend it. + + + 0 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Unconfirmed: + + + true + + + + + + + + 0 + 0 + + + + Your current total balance + + + 0 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Available: + + + true + + + + + + + + 0 + 0 + + + + Total mined coins that have not yet matured. + + + 0 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Immature Stake: + + + true + + + + + + + Immature: + + + true + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 7 + + + + + Staking + + + Qt::PlainText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + 12 + + + + + Blocks: + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Difficulty: + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Net Weight: + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Coin Weight: + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 7 + + + + + Researcher + + + Qt::PlainText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Action Needed + + + + + + + Open the researcher/beacon configuration wizard. + + + false + + + + + + + + + Qt::Horizontal + + + + + + + 12 + + + + + Status: + + + true + + + + + + + + 0 + 0 + + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Magnitude: + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Pending Reward: + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 7 + + + + + Current Polls + + + Qt::PlainText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Recent transactions + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Gridcoin network after a connection is established, but this process has not completed yet. + + + out of sync + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + 1 + 0 + + + + + 0 + 0 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoSelection + + + + + + + + + + + + + Qt::Horizontal - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + 0 + 20 + - + @@ -863,8 +1078,6 @@
clicklabel.h
- - - + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 114a89b516..897ea585cb 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -21,16 +21,21 @@ #include #include -#define DECORATION_SIZE 48 +#define DECORATION_SIZE 40 class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - TxViewDelegate(QObject *parent=nullptr, int scaledDecorationSize = DECORATION_SIZE): - QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC), scaledDecorationSize(scaledDecorationSize) + TxViewDelegate(QObject *parent = nullptr) : QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC) { + QPaintDevice *paintDevice = dynamic_cast(parent); + m_decoration_size = GRC::ScalePx(paintDevice, DECORATION_SIZE); + m_padding_y = GRC::ScalePx(paintDevice, 6); + m_offset_x = m_decoration_size + GRC::ScalePx(paintDevice, 8); + m_height = m_decoration_size + (m_padding_y * 2); + m_half_height = (m_height - (m_padding_y * 2)) / 2; } inline void paint(QPainter *painter, const QStyleOptionViewItem &option, @@ -38,14 +43,15 @@ class TxViewDelegate : public QAbstractItemDelegate { painter->save(); + // Paint the theme's background color for the hover state: + const QStyle* const style = option.widget->style(); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); + QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); QRect mainRect = option.rect; - QRect decorationRect(mainRect.topLeft(), QSize(scaledDecorationSize, scaledDecorationSize)); - int xspace = scaledDecorationSize + 8; - int ypad = 6; - int halfheight = (mainRect.height() - 2*ypad)/2; - QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight); - QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight); + QRect decorationRect(mainRect.left(), mainRect.top() + m_padding_y, m_decoration_size, m_decoration_size); + QRect amountRect(mainRect.left() + m_offset_x, mainRect.top() + m_padding_y, mainRect.width() - m_offset_x, m_half_height); + QRect addressRect(mainRect.left() + m_offset_x, mainRect.top() + m_padding_y + m_half_height, mainRect.width() - m_offset_x, m_half_height); icon.paint(painter, decorationRect); QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); @@ -91,15 +97,22 @@ class TxViewDelegate : public QAbstractItemDelegate inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - return QSize(scaledDecorationSize, scaledDecorationSize); + return QSize(m_decoration_size, m_height); + } + + int height() const + { + return m_height; } int unit; private: - - int scaledDecorationSize; - + int m_decoration_size; + int m_padding_y; + int m_offset_x; + int m_height; + int m_half_height; }; #include "overviewpage.moc" @@ -113,41 +126,42 @@ OverviewPage::OverviewPage(QWidget *parent) : currentUnconfirmedBalance(-1), currentImmatureBalance(-1) { - scaledDecorationSize = GRC::ScalePx(this, DECORATION_SIZE); - - txdelegate = new TxViewDelegate(this, scaledDecorationSize); + txdelegate = new TxViewDelegate(this); ui->setupUi(this); - GRC::ScaleFontPointSize(ui->overviewWalletLabel, 15); - GRC::ScaleFontPointSize(ui->researcherHeaderLabel, 15); - GRC::ScaleFontPointSize(ui->stakingHeaderLabel, 15); - GRC::ScaleFontPointSize(ui->recentTransLabel, 15); + GRC::ScaleFontPointSize(ui->headerTitleLabel, 15); + GRC::ScaleFontPointSize(ui->cpidLabel, 9); + GRC::ScaleFontPointSize(ui->headerBalanceLabel, 14); + GRC::ScaleFontPointSize(ui->headerBalanceCaptionLabel, 8); + GRC::ScaleFontPointSize(ui->headerMagnitudeLabel, 14); + GRC::ScaleFontPointSize(ui->headerMagnitudeCaptionLabel, 8); + GRC::ScaleFontPointSize(ui->overviewWalletLabel, 11); + GRC::ScaleFontPointSize(ui->researcherHeaderLabel, 11); + GRC::ScaleFontPointSize(ui->stakingHeaderLabel, 11); + GRC::ScaleFontPointSize(ui->currentPollsHeaderLabel, 11); + GRC::ScaleFontPointSize(ui->recentTransLabel, 11); + GRC::ScaleFontPointSize(ui->walletStatusLabel, 8); + GRC::ScaleFontPointSize(ui->transactionsStatusLabel, 8); + GRC::ScaleFontPointSize(ui->researcherAlertLabel, 8); // Override .ui default spacing to deal with various dpi displays. int verticalSpacing = GRC::ScalePx(this, 7); - ui->verticalLayout_10->setMargin(verticalSpacing); ui->walletGridLayout->setVerticalSpacing(verticalSpacing); ui->stakingGridLayout->setVerticalSpacing(verticalSpacing); ui->researcherGridLayout->setVerticalSpacing(verticalSpacing); - QRect verticalSpacerSpacing(0, 0, 20, GRC::ScalePx(this, 20)); - ui->verticalSpacer->setGeometry(verticalSpacerSpacing); - ui->researcherSectionVerticalSpacer->setGeometry(verticalSpacerSpacing); - // Recent transactions ui->listTransactions->setItemDelegate(txdelegate); - ui->listTransactions->setIconSize(QSize(scaledDecorationSize, scaledDecorationSize)); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); updateTransactions(); connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); - - connect(ui->pollLabel, SIGNAL(clicked()), this, SLOT(handlePollLabelClicked())); + connect(ui->currentPollsTitleLabel, SIGNAL(clicked()), this, SLOT(handlePollLabelClicked())); // init "out of sync" warning labels - ui->walletStatusLabel->setText("(" + tr("out of sync") + ")"); - ui->transactionsStatusLabel->setText("(" + tr("out of sync") + ")"); + ui->walletStatusLabel->setText(tr("out of sync")); + ui->transactionsStatusLabel->setText(tr("out of sync")); // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); @@ -181,7 +195,7 @@ int OverviewPage::getNumTransactionsForView() { // Compute the maximum number of transactions the transaction list widget // can hold without overflowing. - const size_t itemHeight = scaledDecorationSize + ui->listTransactions->spacing(); + const size_t itemHeight = txdelegate->height() + ui->listTransactions->spacing(); const size_t contentsHeight = ui->listTransactions->height(); const int numItems = contentsHeight / itemHeight; @@ -238,6 +252,7 @@ void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBa currentStake = stake; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; + ui->headerBalanceLabel->setText(BitcoinUnits::formatOverviewRounded(balance)); ui->balanceLabel->setText(BitcoinUnits::formatWithUnit(unit, balance)); ui->stakeLabel->setText(BitcoinUnits::formatWithUnit(unit, stake)); ui->unconfirmedLabel->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); @@ -264,7 +279,7 @@ void OverviewPage::setResearcherModel(ResearcherModel *researcherModel) connect(researcherModel, SIGNAL(magnitudeChanged()), this, SLOT(updateMagnitude())); connect(researcherModel, SIGNAL(accrualChanged()), this, SLOT(updatePendingAccrual())); connect(researcherModel, SIGNAL(beaconChanged()), this, SLOT(updateResearcherAlert())); - connect(ui->beaconButton, SIGNAL(clicked()), this, SLOT(onBeaconButtonClicked())); + connect(ui->researcherConfigToolButton, SIGNAL(clicked()), this, SLOT(onBeaconButtonClicked())); } void OverviewPage::setWalletModel(WalletModel *model) @@ -319,7 +334,24 @@ void OverviewPage::updateResearcherStatus() } ui->statusLabel->setText(researcherModel->formatStatus()); - ui->cpidLabel->setText(researcherModel->formatCpid()); + + if (researcherModel->hasEligibleProjects()) { + ui->cpidTextLabel->setText("CPID"); + ui->cpidLabel->setText(researcherModel->formatCpid()); + ui->cpidLabel->setVisible(true); + ui->headerMagnitudeWrapper->setVisible(true); + ui->headerMagnitudeVLine->setVisible(true); + } else if (researcherModel->hasPoolProjects()) { + ui->cpidTextLabel->setText(tr("Pool")); + ui->cpidLabel->setVisible(false); + ui->headerMagnitudeWrapper->setVisible(false); + ui->headerMagnitudeVLine->setVisible(false); + } else { + ui->cpidTextLabel->setText(tr("Staking Only")); + ui->cpidLabel->setVisible(false); + ui->headerMagnitudeWrapper->setVisible(false); + ui->headerMagnitudeVLine->setVisible(false); + } updateMagnitude(); updatePendingAccrual(); @@ -332,7 +364,10 @@ void OverviewPage::updateMagnitude() return; } - ui->magnitudeLabel->setText(researcherModel->formatMagnitude()); + const QString magnitude = researcherModel->formatMagnitude(); + + ui->headerMagnitudeLabel->setText(magnitude); + ui->magnitudeLabel->setText(magnitude); } void OverviewPage::updatePendingAccrual() @@ -356,7 +391,12 @@ void OverviewPage::updateResearcherAlert() return; } - ui->researcherAlertWrapper->setVisible(researcherModel->actionNeeded()); + const bool action_needed = researcherModel->actionNeeded(); + + ui->researcherAlertLabel->setVisible(action_needed); + ui->researcherConfigToolButton->setProperty("actionNeeded", action_needed); + ui->researcherConfigToolButton->style()->unpolish(ui->researcherConfigToolButton); + ui->researcherConfigToolButton->style()->polish(ui->researcherConfigToolButton); } void OverviewPage::onBeaconButtonClicked() @@ -390,7 +430,7 @@ void OverviewPage::updateGlobalStatus() } // GetCurrentPollTitle() locks cs_main: - ui->pollLabel->setText(QString::fromStdString(GRC::GetCurrentPollTitle()) + ui->currentPollsTitleLabel->setText(QString::fromStdString(GRC::GetCurrentPollTitle()) .left(80) .replace(QChar('_'), QChar(' '), Qt::CaseSensitive)); } diff --git a/src/qt/res/icons/icons_dark/settings_action_needed.svg b/src/qt/res/icons/icons_dark/settings_action_needed.svg new file mode 100644 index 0000000000..e86183a657 --- /dev/null +++ b/src/qt/res/icons/icons_dark/settings_action_needed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/qt/res/icons/icons_dark/sidebar_settings_active.svg b/src/qt/res/icons/icons_dark/sidebar_settings_active.svg new file mode 100644 index 0000000000..fdb7ba34d2 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_settings_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sidebar_settings_inactive.svg b/src/qt/res/icons/icons_dark/sidebar_settings_inactive.svg new file mode 100644 index 0000000000..55a514de5d --- /dev/null +++ b/src/qt/res/icons/icons_dark/sidebar_settings_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/settings.svg b/src/qt/res/icons/icons_light/settings.svg new file mode 100644 index 0000000000..d9492dd637 --- /dev/null +++ b/src/qt/res/icons/icons_light/settings.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/settings_action_needed.svg b/src/qt/res/icons/icons_light/settings_action_needed.svg new file mode 100644 index 0000000000..53d3621e47 --- /dev/null +++ b/src/qt/res/icons/icons_light/settings_action_needed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/qt/res/icons/icons_light/sidebar_settings_active.svg b/src/qt/res/icons/icons_light/sidebar_settings_active.svg new file mode 100644 index 0000000000..d472b1feaf --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_settings_active.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/icons_light/sidebar_settings_inactive.svg b/src/qt/res/icons/icons_light/sidebar_settings_inactive.svg new file mode 100644 index 0000000000..083f55f6d2 --- /dev/null +++ b/src/qt/res/icons/icons_light/sidebar_settings_inactive.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/tx_input.svg b/src/qt/res/icons/tx_input.svg index 03eefd227b..32180b0785 100644 --- a/src/qt/res/icons/tx_input.svg +++ b/src/qt/res/icons/tx_input.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/qt/res/icons/tx_output.svg b/src/qt/res/icons/tx_output.svg index 68e7c56df8..2072f6814d 100644 --- a/src/qt/res/icons/tx_output.svg +++ b/src/qt/res/icons/tx_output.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 1e63f5c9ff..5434de68ee 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -43,17 +43,19 @@ TransactionView { background: rgb(23, 30, 40); border: none; border-radius: 0.26em; - padding: 0.75em; + padding: 0.75em 1em; } /* HLine */ QFrame[frameShape="4"] { - border-top: 0.065em solid rgb(58, 70, 94); + border-top: 0.065em solid rgba(255, 255, 255, 0.1); + background: transparent; } /* VLine */ QFrame[frameShape="5"] { - border-left: 0.065em solid rgb(58, 70, 94); + border-left: 0.065em solid rgba(255, 255, 255, 0.1); + background: transparent; } QAbstractItemView { @@ -355,6 +357,11 @@ QToolButton { padding: 0.25em; } +ClickLabel { + color: palette(link); + text-decoration: underline; +} + QDateEdit::up-button, QDateEdit::down-button, QDoubleSpinBox::up-button, @@ -524,6 +531,10 @@ QStatusBar QToolTip { font-weight: bold; } +#statusbarAlertsLabel { + color: white; +} + #aboutLogoLabel{ image:url(:/images/about); min-width:2.25em; @@ -532,20 +543,74 @@ QStatusBar QToolTip { max-height:9.225em; } +#headerFrame { + background: none; + border-bottom: 0.065em solid rgb(18, 26, 34); + border-radius: 0; + min-height: 4em; + max-height: 4em; + padding: 0 1em; +} + +/* VLine */ +#headerFrame QFrame[frameShape="5"] { + margin: 1em 0; +} + +#headerFrame #headerTitleLabel { + color: white; + font-weight: 500; +} + +#contentFrame { + background: none; + max-width: 60em; + padding: 0; + margin: 0; +} + +#currentPollsFrame, +#recentTransactionsFrame, +#researcherFrame, +#stakingFrame, +#walletFrame { + border-bottom: 0.065em solid rgba(0, 0, 0, 0.4); +} + /* overview page */ +#currentPollsHeaderLabel, #overviewWalletLabel, #researcherHeaderLabel, #stakingHeaderLabel, #recentTransLabel { - font-weight: bold; - color: rgb(250, 250, 250); + font-weight: 500; + color: rgb(235, 236, 238); } #OverviewPage QLabel[isRowHeader=true] { min-width: 6.5em; } +#OverviewPage #cpidTextLabel { + border: 0.065em solid rgb(250, 250, 250); + border-radius: 0.5em; + margin-right: 0.25em; + padding: 0 0.1em; + color: rgb(250, 250, 250); +} + +#OverviewPage #headerBalanceLabel, +#OverviewPage #headerMagnitudeLabel { + color: rgb(26, 145, 235); + font-weight: 500; +} + +#OverviewPage #headerBalanceCaptionLabel, +#OverviewPage #headerMagnitudeCaptionLabel { + color: rgb(183, 189, 190); +} + #availableLabel, #immatureTextLabel, #totalBalanceLabel { @@ -555,13 +620,41 @@ QStatusBar QToolTip { #walletStatusLabel, #transactionsStatusLabel, #researcherAlertLabel { - color: red; + background-color: rgb(237, 81, 68); + border-radius: 0.25em; + padding: 0 0.2em; + color: white; + font-weight: bold; +} + +#researcherConfigToolButton { + background: none; + border: none; + padding: 0.1em; + width: 1em; + height: 1em; +} + +#researcherConfigToolButton:hover { + background-color: rgb(21, 126, 205); +} + +#researcherConfigToolButton { + image: url(:/icons/settings_dark); +} + +#researcherConfigToolButton[actionNeeded=true] { + image: url(:/icons/dark_settings_action_needed); } #listTransactions { background: none; } +#listTransactions::item { + border-radius: 0.26em; +} + /* coincontrol dialog*/ #coinControlFeaturesLabel{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 014e112e4a..36cc683abc 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -39,7 +39,7 @@ TransactionView { background: white; border: none; border-radius: 0.26em; - padding: 0.75em; + padding: 0.75em 1em; } /* HLine */ @@ -365,6 +365,11 @@ QToolButton { color: rgb(97, 101, 118); } +ClickLabel { + color: palette(link); + text-decoration: underline; +} + QDateEdit::up-button, QDateEdit::down-button, QDoubleSpinBox::up-button, @@ -541,8 +546,43 @@ QStatusBar .QFrame QLabel { max-height:9.225em; } +#headerFrame { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(240, 240, 240), stop: 0.1 rgb(255, 255, 255)); + border-bottom: 0.065em solid rgb(230, 230, 230); + border-radius: 0; + min-height: 4em; + max-height: 4em; + padding: 0 1em; +} + +/* VLine */ +#headerFrame QFrame[frameShape="5"] { + margin: 1em 0; +} + +#headerFrame #headerTitleLabel { + color: rgb(43, 52, 69); + font-weight: 500; +} + +#contentFrame { + background: none; + max-width: 60em; + padding: 0; + margin: 0; +} + +#currentPollsFrame, +#recentTransactionsFrame, +#researcherFrame, +#stakingFrame, +#walletFrame { + border-bottom: 0.065em solid rgba(0, 0, 0, 0.1); +} + /* overview page */ +#currentPollsHeaderLabel, #overviewWalletLabel, #researcherHeaderLabel, #stakingHeaderLabel, @@ -555,6 +595,20 @@ QStatusBar .QFrame QLabel { min-width: 6.5em; } +#OverviewPage #cpidTextLabel { + border: 0.065em solid rgb(43, 52, 69); + border-radius: 0.5em; + margin-right: 0.25em; + padding: 0 0.1em; + color: rgb(43, 52, 69); +} + +#OverviewPage #headerBalanceLabel, +#OverviewPage #headerMagnitudeLabel { + color: rgb(140, 20, 254); + font-weight: 500; +} + #availableLabel, #immatureTextLabel, #totalBalanceLabel { @@ -564,13 +618,41 @@ QStatusBar .QFrame QLabel { #walletStatusLabel, #transactionsStatusLabel, #researcherAlertLabel { - color: red; + background-color: rgb(237, 81, 68); + border-radius: 0.25em; + padding: 0 0.2em; + color: white; + font-weight: bold; +} + +#researcherConfigToolButton { + background: none; + border: none; + padding: 0.1em; + width: 1em; + height: 1em; +} + +#researcherConfigToolButton:hover { + background: rgb(241, 245, 247); +} + +#researcherConfigToolButton { + image: url(:/icons/light_settings); +} + +#researcherConfigToolButton[actionNeeded=true] { + image: url(:/icons/light_settings_action_needed); } #listTransactions { background: none; } +#listTransactions::item { + border-radius: 0.26em; +} + /* coincontrol dialog*/ #coinControlFeaturesLabel{ From 73871773e6456f00d690cce2f7699d3084eebdb6 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 25 Apr 2021 01:11:42 -0500 Subject: [PATCH 081/280] Tweak GUI overview capitalization and adjust translations This changes the text for the following phrases on the overview page: - the "Recent transactions" label to "Recent Transactions" - the "out of sync" label to "Out of Sync" - the "Current Poll:" label to "Current Polls" ...and updates the localization metadata keys. Because the meanings of the phrases did not change, this preserves the existing translations for those entries. --- src/qt/forms/overviewpage.ui | 6 +++--- src/qt/locale/bitcoin_af_ZA.ts | 6 +++--- src/qt/locale/bitcoin_ar.ts | 6 +++--- src/qt/locale/bitcoin_be_BY.ts | 6 +++--- src/qt/locale/bitcoin_bg.ts | 6 +++--- src/qt/locale/bitcoin_bs.ts | 6 +++--- src/qt/locale/bitcoin_ca.ts | 6 +++--- src/qt/locale/bitcoin_ca@valencia.ts | 6 +++--- src/qt/locale/bitcoin_ca_ES.ts | 6 +++--- src/qt/locale/bitcoin_cs.ts | 6 +++--- src/qt/locale/bitcoin_cy.ts | 6 +++--- src/qt/locale/bitcoin_da.ts | 6 +++--- src/qt/locale/bitcoin_de.ts | 6 +++--- src/qt/locale/bitcoin_el_GR.ts | 6 +++--- src/qt/locale/bitcoin_en.ts | 8 ++++---- src/qt/locale/bitcoin_eo.ts | 6 +++--- src/qt/locale/bitcoin_es.ts | 6 +++--- src/qt/locale/bitcoin_es_CL.ts | 6 +++--- src/qt/locale/bitcoin_es_DO.ts | 6 +++--- src/qt/locale/bitcoin_es_MX.ts | 6 +++--- src/qt/locale/bitcoin_es_UY.ts | 6 +++--- src/qt/locale/bitcoin_et.ts | 6 +++--- src/qt/locale/bitcoin_eu_ES.ts | 6 +++--- src/qt/locale/bitcoin_fa.ts | 6 +++--- src/qt/locale/bitcoin_fa_IR.ts | 6 +++--- src/qt/locale/bitcoin_fi.ts | 6 +++--- src/qt/locale/bitcoin_fr.ts | 8 ++++---- src/qt/locale/bitcoin_fr_CA.ts | 6 +++--- src/qt/locale/bitcoin_gl.ts | 6 +++--- src/qt/locale/bitcoin_he.ts | 6 +++--- src/qt/locale/bitcoin_hi_IN.ts | 6 +++--- src/qt/locale/bitcoin_hr.ts | 6 +++--- src/qt/locale/bitcoin_hu.ts | 6 +++--- src/qt/locale/bitcoin_id_ID.ts | 6 +++--- src/qt/locale/bitcoin_it.ts | 8 ++++---- src/qt/locale/bitcoin_ja.ts | 6 +++--- src/qt/locale/bitcoin_ka.ts | 6 +++--- src/qt/locale/bitcoin_kk_KZ.ts | 6 +++--- src/qt/locale/bitcoin_ko_KR.ts | 6 +++--- src/qt/locale/bitcoin_ky.ts | 6 +++--- src/qt/locale/bitcoin_la.ts | 6 +++--- src/qt/locale/bitcoin_lt.ts | 6 +++--- src/qt/locale/bitcoin_lv_LV.ts | 6 +++--- src/qt/locale/bitcoin_ms_MY.ts | 6 +++--- src/qt/locale/bitcoin_nb.ts | 6 +++--- src/qt/locale/bitcoin_nl.ts | 6 +++--- src/qt/locale/bitcoin_pam.ts | 6 +++--- src/qt/locale/bitcoin_pl.ts | 8 ++++---- src/qt/locale/bitcoin_pt_BR.ts | 6 +++--- src/qt/locale/bitcoin_pt_PT.ts | 10 +++++----- src/qt/locale/bitcoin_ro_RO.ts | 6 +++--- src/qt/locale/bitcoin_ru.ts | 8 ++++---- src/qt/locale/bitcoin_sk.ts | 6 +++--- src/qt/locale/bitcoin_sl_SI.ts | 6 +++--- src/qt/locale/bitcoin_sq.ts | 6 +++--- src/qt/locale/bitcoin_sr.ts | 6 +++--- src/qt/locale/bitcoin_sv.ts | 8 ++++---- src/qt/locale/bitcoin_th_TH.ts | 6 +++--- src/qt/locale/bitcoin_tr.ts | 6 +++--- src/qt/locale/bitcoin_uk.ts | 6 +++--- src/qt/locale/bitcoin_ur_PK.ts | 6 +++--- src/qt/locale/bitcoin_vi.ts | 6 +++--- src/qt/locale/bitcoin_vi_VN.ts | 6 +++--- src/qt/locale/bitcoin_zh_CN.ts | 8 ++++---- src/qt/locale/bitcoin_zh_TW.ts | 6 +++--- src/qt/overviewpage.cpp | 6 +++--- 66 files changed, 207 insertions(+), 207 deletions(-) diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index b0192885df..3ac9045a85 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -346,7 +346,7 @@ The displayed information may be out of date. Your wallet automatically synchronizes with the Gridcoin network after a connection is established, but this process has not completed yet. - out of sync + Out of Sync Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -966,7 +966,7 @@ - Recent transactions + Recent Transactions @@ -1004,7 +1004,7 @@ The displayed information may be out of date. Your wallet automatically synchronizes with the Gridcoin network after a connection is established, but this process has not completed yet. - out of sync + Out of Sync Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/src/qt/locale/bitcoin_af_ZA.ts b/src/qt/locale/bitcoin_af_ZA.ts index ab054358f1..fe8abd2c0f 100644 --- a/src/qt/locale/bitcoin_af_ZA.ts +++ b/src/qt/locale/bitcoin_af_ZA.ts @@ -1909,18 +1909,18 @@ Dit beteken dat 'n fooi van ten minste %2 word benodig. - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync uit sinchro diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts index 4d8f489ed9..a8c91646cf 100644 --- a/src/qt/locale/bitcoin_ar.ts +++ b/src/qt/locale/bitcoin_ar.ts @@ -1881,12 +1881,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1927,7 +1927,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_be_BY.ts b/src/qt/locale/bitcoin_be_BY.ts index b04220b407..ef613228b9 100644 --- a/src/qt/locale/bitcoin_be_BY.ts +++ b/src/qt/locale/bitcoin_be_BY.ts @@ -1898,18 +1898,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts index f12e9be8f6..b69a3f1b19 100644 --- a/src/qt/locale/bitcoin_bg.ts +++ b/src/qt/locale/bitcoin_bg.ts @@ -1836,7 +1836,7 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Скорошни транзакции @@ -1897,13 +1897,13 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_bs.ts b/src/qt/locale/bitcoin_bs.ts index a90f645fb6..c04834f65b 100644 --- a/src/qt/locale/bitcoin_bs.ts +++ b/src/qt/locale/bitcoin_bs.ts @@ -1893,12 +1893,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1909,7 +1909,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts index bd0651d947..d243edcafe 100644 --- a/src/qt/locale/bitcoin_ca.ts +++ b/src/qt/locale/bitcoin_ca.ts @@ -1836,7 +1836,7 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Transaccions recents @@ -1897,13 +1897,13 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ca@valencia.ts b/src/qt/locale/bitcoin_ca@valencia.ts index 817c729443..527b7725ce 100644 --- a/src/qt/locale/bitcoin_ca@valencia.ts +++ b/src/qt/locale/bitcoin_ca@valencia.ts @@ -1836,7 +1836,7 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Transaccions recents @@ -1897,13 +1897,13 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts index ca6b7e74e7..490e5d62d1 100644 --- a/src/qt/locale/bitcoin_ca_ES.ts +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -1850,7 +1850,7 @@ En aquest cas es requereix una comisió d'almenys 2%. - Recent transactions + Recent Transactions Transaccions recents @@ -1911,13 +1911,13 @@ En aquest cas es requereix una comisió d'almenys 2%. - Current Poll: + Current Polls - out of sync + Out of Sync Fora de sincronia diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index 48df5af730..8640f5dc1f 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -1845,7 +1845,7 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Poslední transakce @@ -1906,13 +1906,13 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls - out of sync + Out of Sync nesynchronizováno diff --git a/src/qt/locale/bitcoin_cy.ts b/src/qt/locale/bitcoin_cy.ts index df8265a03d..6781e200ac 100644 --- a/src/qt/locale/bitcoin_cy.ts +++ b/src/qt/locale/bitcoin_cy.ts @@ -1910,18 +1910,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 1570678dd8..8f0d467f51 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -1853,7 +1853,7 @@ Det betyder, at et gebyr på mindst %2 er påkrævet. - Recent transactions + Recent Transactions Nylige transaktioner @@ -1914,13 +1914,13 @@ Det betyder, at et gebyr på mindst %2 er påkrævet. - Current Poll: + Current Polls - out of sync + Out of Sync ikke synkroniseret diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index 2d6c9c8b42..dea2dddee2 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -1855,7 +1855,7 @@ Dieses Label wird rot, wenn die Priorität kleiner ist als Mittel. - Recent transactions + Recent Transactions Letzte Transaktionen @@ -1916,13 +1916,13 @@ Dieses Label wird rot, wenn die Priorität kleiner ist als Mittel. - Current Poll: + Current Polls - out of sync + Out of Sync nicht synchron diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts index e70f357feb..7ac964749f 100644 --- a/src/qt/locale/bitcoin_el_GR.ts +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -1837,7 +1837,7 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Πρόσφατες συναλλαγές @@ -1898,13 +1898,13 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 56a58ce0cc..4892eedc46 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -1895,19 +1895,19 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync - out of sync + Out of Sync + Out of Sync diff --git a/src/qt/locale/bitcoin_eo.ts b/src/qt/locale/bitcoin_eo.ts index 7a7e364a5a..a2f8e0bed4 100644 --- a/src/qt/locale/bitcoin_eo.ts +++ b/src/qt/locale/bitcoin_eo.ts @@ -1825,7 +1825,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1900,13 +1900,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Lastaj transakcioj - out of sync + Out of Sync nesinkronigita diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 590d2c31e2..65f199a579 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -1835,7 +1835,7 @@ Esto significa que se requiere una cuota de al menos %2. - Current Poll: + Current Polls @@ -1915,13 +1915,13 @@ Esto significa que se requiere una cuota de al menos %2. - Recent transactions + Recent Transactions Transacciones recientes - out of sync + Out of Sync desincronizado diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts index eeeab867d5..c4515a9c79 100644 --- a/src/qt/locale/bitcoin_es_CL.ts +++ b/src/qt/locale/bitcoin_es_CL.ts @@ -1892,12 +1892,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1908,7 +1908,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync desincronizado diff --git a/src/qt/locale/bitcoin_es_DO.ts b/src/qt/locale/bitcoin_es_DO.ts index aa0b44a9e6..8787fae171 100644 --- a/src/qt/locale/bitcoin_es_DO.ts +++ b/src/qt/locale/bitcoin_es_DO.ts @@ -1860,12 +1860,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1906,7 +1906,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync desincronizado diff --git a/src/qt/locale/bitcoin_es_MX.ts b/src/qt/locale/bitcoin_es_MX.ts index 8799e3774e..035d6f2ee1 100644 --- a/src/qt/locale/bitcoin_es_MX.ts +++ b/src/qt/locale/bitcoin_es_MX.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_es_UY.ts b/src/qt/locale/bitcoin_es_UY.ts index 1226a016a4..784ec6b915 100644 --- a/src/qt/locale/bitcoin_es_UY.ts +++ b/src/qt/locale/bitcoin_es_UY.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts index 9710c5e5ad..a4a6eaee0e 100644 --- a/src/qt/locale/bitcoin_et.ts +++ b/src/qt/locale/bitcoin_et.ts @@ -1865,7 +1865,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1900,13 +1900,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Hiljutised tehingud - out of sync + Out of Sync sünkimata diff --git a/src/qt/locale/bitcoin_eu_ES.ts b/src/qt/locale/bitcoin_eu_ES.ts index 632876279d..0645856ee9 100644 --- a/src/qt/locale/bitcoin_eu_ES.ts +++ b/src/qt/locale/bitcoin_eu_ES.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts index 56885d0ce1..50747b2944 100644 --- a/src/qt/locale/bitcoin_fa.ts +++ b/src/qt/locale/bitcoin_fa.ts @@ -1811,7 +1811,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1891,13 +1891,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions تراکنش های اخیر - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts index ef905e4095..fa684d8db2 100644 --- a/src/qt/locale/bitcoin_fa_IR.ts +++ b/src/qt/locale/bitcoin_fa_IR.ts @@ -1856,7 +1856,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1891,13 +1891,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts index 59e417f4f7..e05119d2c1 100644 --- a/src/qt/locale/bitcoin_fi.ts +++ b/src/qt/locale/bitcoin_fi.ts @@ -1834,7 +1834,7 @@ Tämä tarkoittaa, että ainakin %2 rahansiirtopalkkio tarvitaan. - Current Poll: + Current Polls @@ -1914,13 +1914,13 @@ Tämä tarkoittaa, että ainakin %2 rahansiirtopalkkio tarvitaan. - Recent transactions + Recent Transactions Viimeisimmät rahansiirrot - out of sync + Out of Sync Ei ajan tasalla diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index c5fe4bf087..bddaa53342 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -1834,8 +1834,8 @@ Cela implique que des frais à hauteur d'au moins %2 seront nécessaires. - Current Poll: - Sondage: + Current Polls + Sondage @@ -1914,13 +1914,13 @@ Cela implique que des frais à hauteur d'au moins %2 seront nécessaires. - Recent transactions + Recent Transactions Transactions récentes - out of sync + Out of Sync désynchronisé diff --git a/src/qt/locale/bitcoin_fr_CA.ts b/src/qt/locale/bitcoin_fr_CA.ts index 728604d8aa..11621933e0 100644 --- a/src/qt/locale/bitcoin_fr_CA.ts +++ b/src/qt/locale/bitcoin_fr_CA.ts @@ -1909,18 +1909,18 @@ Les montants inférieurs à 0.546 fois les frais minimum de relais apparaissent - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync désynchronisé diff --git a/src/qt/locale/bitcoin_gl.ts b/src/qt/locale/bitcoin_gl.ts index 91f0de7b84..8bd4630c15 100644 --- a/src/qt/locale/bitcoin_gl.ts +++ b/src/qt/locale/bitcoin_gl.ts @@ -1860,12 +1860,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1906,7 +1906,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync non sincronizado diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts index 53efc3ff76..54693595b1 100644 --- a/src/qt/locale/bitcoin_he.ts +++ b/src/qt/locale/bitcoin_he.ts @@ -1817,7 +1817,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1897,13 +1897,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions העברות אחרונות - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_hi_IN.ts b/src/qt/locale/bitcoin_hi_IN.ts index 80007f6d6f..a599275c80 100644 --- a/src/qt/locale/bitcoin_hi_IN.ts +++ b/src/qt/locale/bitcoin_hi_IN.ts @@ -1894,18 +1894,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_hr.ts b/src/qt/locale/bitcoin_hr.ts index 2921117dab..aeb4e0b3b6 100644 --- a/src/qt/locale/bitcoin_hr.ts +++ b/src/qt/locale/bitcoin_hr.ts @@ -1896,12 +1896,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1912,7 +1912,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts index 2d7d389aa6..ebd66d283d 100644 --- a/src/qt/locale/bitcoin_hu.ts +++ b/src/qt/locale/bitcoin_hu.ts @@ -1814,7 +1814,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1894,13 +1894,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions A legutóbbi tranzakciók - out of sync + Out of Sync Nincs szinkronban. diff --git a/src/qt/locale/bitcoin_id_ID.ts b/src/qt/locale/bitcoin_id_ID.ts index dad63da7f0..31fd0c31e3 100644 --- a/src/qt/locale/bitcoin_id_ID.ts +++ b/src/qt/locale/bitcoin_id_ID.ts @@ -1849,12 +1849,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1900,7 +1900,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync tidak tersinkron diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index 0ed1dbf840..ca3af2c93a 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -1834,8 +1834,8 @@ Questa etichetta diventa rossa se la priorità è minore di "media". - Current Poll: - Sondaggio corrente: + Current Polls + Sondaggio Corrente @@ -1914,13 +1914,13 @@ Questa etichetta diventa rossa se la priorità è minore di "media". - Recent transactions + Recent Transactions Transazioni recenti - out of sync + Out of Sync fuori sincrono diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 715527bd9c..442c807bcc 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -1811,7 +1811,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1891,13 +1891,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions 最近のトランザクション - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ka.ts b/src/qt/locale/bitcoin_ka.ts index 3ce93fedf6..8ef65526db 100644 --- a/src/qt/locale/bitcoin_ka.ts +++ b/src/qt/locale/bitcoin_ka.ts @@ -1852,12 +1852,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1903,7 +1903,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_kk_KZ.ts b/src/qt/locale/bitcoin_kk_KZ.ts index 31f1dfa728..75395cdc09 100644 --- a/src/qt/locale/bitcoin_kk_KZ.ts +++ b/src/qt/locale/bitcoin_kk_KZ.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index 70217ba82d..cfa08cc5b3 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -1811,7 +1811,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1891,13 +1891,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions 최근 거래 - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ky.ts b/src/qt/locale/bitcoin_ky.ts index 7c9508db5e..9acc85ea3f 100644 --- a/src/qt/locale/bitcoin_ky.ts +++ b/src/qt/locale/bitcoin_ky.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_la.ts b/src/qt/locale/bitcoin_la.ts index f426ed542d..97c809f7cb 100644 --- a/src/qt/locale/bitcoin_la.ts +++ b/src/qt/locale/bitcoin_la.ts @@ -1890,12 +1890,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1906,7 +1906,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync non synchronizato diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts index 2740000bc1..85143fbe6e 100644 --- a/src/qt/locale/bitcoin_lt.ts +++ b/src/qt/locale/bitcoin_lt.ts @@ -1861,12 +1861,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1912,7 +1912,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync nesinchronizuota diff --git a/src/qt/locale/bitcoin_lv_LV.ts b/src/qt/locale/bitcoin_lv_LV.ts index ce13daf88d..6aa0758e67 100644 --- a/src/qt/locale/bitcoin_lv_LV.ts +++ b/src/qt/locale/bitcoin_lv_LV.ts @@ -1861,12 +1861,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1912,7 +1912,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync nav sinhroniz?ts diff --git a/src/qt/locale/bitcoin_ms_MY.ts b/src/qt/locale/bitcoin_ms_MY.ts index 3cd8a27f43..e03468c0d1 100644 --- a/src/qt/locale/bitcoin_ms_MY.ts +++ b/src/qt/locale/bitcoin_ms_MY.ts @@ -1887,18 +1887,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index 361b2990bd..ed5bf37639 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -1822,7 +1822,7 @@ Dette betyr at det trengs en avgift på minimum %2. - Current Poll: + Current Polls @@ -1902,13 +1902,13 @@ Dette betyr at det trengs en avgift på minimum %2. - Recent transactions + Recent Transactions Nylige transaksjoner - out of sync + Out of Sync ute av synk diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index ac82b70d0f..64a5c8d8b5 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -1834,7 +1834,7 @@ Dit betekend dat een fee van %2 is vereist. - Current Poll: + Current Polls @@ -1914,13 +1914,13 @@ Dit betekend dat een fee van %2 is vereist. - Recent transactions + Recent Transactions Recente transacties - out of sync + Out of Sync niet gesynchroniseerd diff --git a/src/qt/locale/bitcoin_pam.ts b/src/qt/locale/bitcoin_pam.ts index 41d9dcee83..809fc97968 100644 --- a/src/qt/locale/bitcoin_pam.ts +++ b/src/qt/locale/bitcoin_pam.ts @@ -1704,12 +1704,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1735,7 +1735,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync ali ya maka-sync diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index 409f22d8d1..c7400009fe 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -1844,8 +1844,8 @@ Wartości poniżej 0.546*minimalna wartość przekazu. są traktowane jako " - Current Poll: - Aktualne głosowanie: + Current Polls + Aktualne Głosowanie @@ -1924,13 +1924,13 @@ Wartości poniżej 0.546*minimalna wartość przekazu. są traktowane jako " - Recent transactions + Recent Transactions Ostatnie transakcje - out of sync + Out of Sync desynchronizacja diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index da1d13967d..57cce3137e 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -1820,7 +1820,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1900,13 +1900,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Transações recentes - out of sync + Out of Sync fora de sincronia diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index 747e6ec9b6..7c6563673e 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -1903,18 +1903,18 @@ Isto significa que uma taxa de pelo menos %2 é necessária. - Recent transactions + Recent Transactions Transações recentes - Current Poll: - Votação Atual: + Current Polls + Votação Atual - out of sync + Out of Sync fora de sincronia @@ -5939,4 +5939,4 @@ Se o ficheiro não existir, crie-o com permissões de leitura. Erro - \ No newline at end of file + diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts index 917ece8686..06304d15e2 100644 --- a/src/qt/locale/bitcoin_ro_RO.ts +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -1840,7 +1840,7 @@ Acest lucru înseamn? c? o tax? de cel pu?in %2 este necesar? - Current Poll: + Current Polls @@ -1920,13 +1920,13 @@ Acest lucru înseamn? c? o tax? de cel pu?in %2 este necesar? - Recent transactions + Recent Transactions Tranzacţii recente - out of sync + Out of Sync Nu este sincronizat diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index 77a977a716..77823485be 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -1842,8 +1842,8 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: - Текущий Опрос: + Current Polls + Текущий Опрос @@ -1922,13 +1922,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Последние транзакции - out of sync + Out of Sync не синхронизировано diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index c0ef1a06fc..0613131959 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -1840,7 +1840,7 @@ To znamená, že je potrebný poplatok aspoň %2. - Current Poll: + Current Polls @@ -1920,13 +1920,13 @@ To znamená, že je potrebný poplatok aspoň %2. - Recent transactions + Recent Transactions Nedávne transakcie - out of sync + Out of Sync nesynchronizované diff --git a/src/qt/locale/bitcoin_sl_SI.ts b/src/qt/locale/bitcoin_sl_SI.ts index e56820cc91..86175b21ec 100644 --- a/src/qt/locale/bitcoin_sl_SI.ts +++ b/src/qt/locale/bitcoin_sl_SI.ts @@ -1846,7 +1846,7 @@ Ta oznaka se obarva rde?e, ?e je prioriteta manjša kot "srednja". - Current Poll: + Current Polls @@ -1926,13 +1926,13 @@ Ta oznaka se obarva rde?e, ?e je prioriteta manjša kot "srednja". - Recent transactions + Recent Transactions Nedavne transakcije - out of sync + Out of Sync nesinhronizirano diff --git a/src/qt/locale/bitcoin_sq.ts b/src/qt/locale/bitcoin_sq.ts index 846d37d340..69b1f48871 100644 --- a/src/qt/locale/bitcoin_sq.ts +++ b/src/qt/locale/bitcoin_sq.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts index 9ba1126f20..a864301208 100644 --- a/src/qt/locale/bitcoin_sr.ts +++ b/src/qt/locale/bitcoin_sr.ts @@ -1898,18 +1898,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index 229ba366e4..9e2659bb71 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -1835,8 +1835,8 @@ Detta betyder att en avgift på minst %2 krävs. - Current Poll: - Aktuell omröstning: + Current Polls + Aktuell omröstning @@ -1915,13 +1915,13 @@ Detta betyder att en avgift på minst %2 krävs. - Recent transactions + Recent Transactions Nyligen genomförda transaktioner - out of sync + Out of Sync osynkroniserad diff --git a/src/qt/locale/bitcoin_th_TH.ts b/src/qt/locale/bitcoin_th_TH.ts index bd2d6ecc8f..dd6c3a414e 100644 --- a/src/qt/locale/bitcoin_th_TH.ts +++ b/src/qt/locale/bitcoin_th_TH.ts @@ -1886,18 +1886,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index 131fbde190..df7bff22c2 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -1828,7 +1828,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1908,13 +1908,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Son işlemler - out of sync + Out of Sync Eşleşme Dışı diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index 77fa7771f9..2ee52e3060 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -1823,7 +1823,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1903,13 +1903,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions Останні транзакції - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_ur_PK.ts b/src/qt/locale/bitcoin_ur_PK.ts index 8a0c0aa5a5..12f9733c5f 100644 --- a/src/qt/locale/bitcoin_ur_PK.ts +++ b/src/qt/locale/bitcoin_ur_PK.ts @@ -1892,18 +1892,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_vi.ts b/src/qt/locale/bitcoin_vi.ts index 4d8510e628..e12155cb83 100644 --- a/src/qt/locale/bitcoin_vi.ts +++ b/src/qt/locale/bitcoin_vi.ts @@ -1886,18 +1886,18 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_vi_VN.ts b/src/qt/locale/bitcoin_vi_VN.ts index 12d60aa656..ab56e7a27c 100644 --- a/src/qt/locale/bitcoin_vi_VN.ts +++ b/src/qt/locale/bitcoin_vi_VN.ts @@ -1876,12 +1876,12 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - Current Poll: + Current Polls @@ -1897,7 +1897,7 @@ This label turns red, if the priority is smaller than "medium". - out of sync + Out of Sync diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 65f1d3a546..7e25761a05 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -1823,8 +1823,8 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: - 当前民意调查: + Current Polls + 当前民意调查 @@ -1903,13 +1903,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions - out of sync + Out of Sync 未完成同步 diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 95d8bdf064..686e79b544 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -1811,7 +1811,7 @@ This label turns red, if the priority is smaller than "medium". - Current Poll: + Current Polls @@ -1891,13 +1891,13 @@ This label turns red, if the priority is smaller than "medium". - Recent transactions + Recent Transactions 最近的交易 - out of sync + Out of Sync diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 897ea585cb..ad0fcb6522 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -151,7 +151,7 @@ OverviewPage::OverviewPage(QWidget *parent) : ui->stakingGridLayout->setVerticalSpacing(verticalSpacing); ui->researcherGridLayout->setVerticalSpacing(verticalSpacing); - // Recent transactions + // Recent Transactions ui->listTransactions->setItemDelegate(txdelegate); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); updateTransactions(); @@ -160,8 +160,8 @@ OverviewPage::OverviewPage(QWidget *parent) : connect(ui->currentPollsTitleLabel, SIGNAL(clicked()), this, SLOT(handlePollLabelClicked())); // init "out of sync" warning labels - ui->walletStatusLabel->setText(tr("out of sync")); - ui->transactionsStatusLabel->setText(tr("out of sync")); + ui->walletStatusLabel->setText(tr("Out of Sync")); + ui->transactionsStatusLabel->setText(tr("Out of Sync")); // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); From 77f222645bbebc62706886519e9c2daa07908725 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 29 Apr 2021 10:05:41 -0500 Subject: [PATCH 082/280] Add GUI light/dark theme toggle button This adds a button next to the logo in the sidebar that switches the active GUI theme between light and dark mode. --- src/Makefile.qt.include | 4 +++ src/qt/bitcoin.qrc | 5 +++ src/qt/bitcoingui.cpp | 38 ++++++++++++++++++--- src/qt/bitcoingui.h | 2 ++ src/qt/optionsmodel.cpp | 5 +++ src/qt/optionsmodel.h | 3 ++ src/qt/res/icons/dark_mode.svg | 4 +++ src/qt/res/icons/dark_mode_active.svg | 4 +++ src/qt/res/icons/light_mode.svg | 4 +++ src/qt/res/icons/light_mode_active.svg | 4 +++ src/qt/res/stylesheets/dark_stylesheet.qss | 32 ++++++++++++++--- src/qt/res/stylesheets/light_stylesheet.qss | 32 ++++++++++++++--- 12 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 src/qt/res/icons/dark_mode.svg create mode 100644 src/qt/res/icons/dark_mode_active.svg create mode 100644 src/qt/res/icons/light_mode.svg create mode 100644 src/qt/res/icons/light_mode_active.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 9c0cab65e7..2c7aa67182 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -306,6 +306,8 @@ RES_ICONS = \ qt/res/icons/connect2.svg \ qt/res/icons/connect3.svg \ qt/res/icons/connect4.svg \ + qt/res/icons/dark_mode.svg \ + qt/res/icons/dark_mode_active.svg \ qt/res/icons/debugwindow.png \ qt/res/icons/edit.png \ qt/res/icons/editcopy.png \ @@ -316,6 +318,8 @@ RES_ICONS = \ qt/res/icons/gridcoin.ico \ qt/res/icons/gridcoin_testnet.ico \ qt/res/icons/key.png \ + qt/res/icons/light_mode.svg \ + qt/res/icons/light_mode_active.svg \ qt/res/icons/menu.svg \ qt/res/icons/menu_active.svg \ qt/res/icons/message.svg \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index d21a98ff37..610bf38e7d 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -171,6 +171,11 @@ res/icons/message.svg res/icons/menu.svg res/icons/menu_active.svg + res/icons/dark_mode.svg + res/icons/dark_mode_active.svg + res/icons/light_mode.svg + res/icons/light_mode_active.svg +
res/images/splash3.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 8ca96f813d..a3f984d69c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -588,22 +588,47 @@ void BitcoinGUI::createToolBars() { ClickLabel *logoLabel = new ClickLabel(); logoLabel->setObjectName("toolbarLogoLabel"); + QSizePolicy logoLabelSizePolicy = logoLabel->sizePolicy(); + logoLabelSizePolicy.setHorizontalStretch(2); + logoLabel->setSizePolicy(logoLabelSizePolicy); connect(logoLabel, SIGNAL(clicked()), this, SLOT(websiteClicked())); + QHBoxLayout *logoWrapperLayout = new QHBoxLayout(); + logoWrapperLayout->setContentsMargins(2, 0, 2, 0); + logoWrapperLayout->setSpacing(0); + QWidget *logoWrapper = new QWidget(); logoWrapper->setObjectName("toolbarLogoWrapper"); - logoWrapper->setLayout(new QVBoxLayout()); - logoWrapper->layout()->addWidget(logoLabel); + logoWrapper->setLayout(logoWrapperLayout); #ifndef Q_OS_MAC // Windows and Linux: collapse the main application's menu bar into a menu // button. On macOS, we'll continue to use the system's separate menu bar. - QPushButton *menuButton = new QPushButton(logoWrapper); + QPushButton *menuButton = new QPushButton(); menuButton->setObjectName("toolbarMenuButton"); - menuButton->resize(GRC::ScaleSize(this, 24)); + menuButton->setToolTip(tr("Open menu.")); menuButton->setMenu(appMenuBar); + QSizePolicy menuButtonSizePolicy = menuButton->sizePolicy(); + menuButtonSizePolicy.setHorizontalStretch(1); + menuButton->setSizePolicy(menuButtonSizePolicy); + logoWrapperLayout->addWidget(menuButton); + logoWrapperLayout->setAlignment(menuButton, Qt::AlignHCenter | Qt::AlignVCenter); +#else + logoWrapperLayout->addStretch(1); #endif + logoWrapperLayout->addWidget(logoLabel); + + QPushButton *themeToggleButton = new QPushButton(); + themeToggleButton->setObjectName("themeToggleButton"); + themeToggleButton->setToolTip(tr("Toggle light/dark mode.")); + QSizePolicy themeToggleButtonSizePolicy = themeToggleButton->sizePolicy(); + themeToggleButtonSizePolicy.setHorizontalStretch(1); + themeToggleButton->setSizePolicy(themeToggleButtonSizePolicy); + connect(themeToggleButton, SIGNAL(clicked()), this, SLOT(themeToggled())); + logoWrapperLayout->addWidget(themeToggleButton); + logoWrapperLayout->setAlignment(themeToggleButton, Qt::AlignHCenter | Qt::AlignVCenter); + QWidget *boincLabelSpacer = new QWidget(); boincLabelSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -863,6 +888,11 @@ void BitcoinGUI::optionsClicked() dlg.exec(); } +void BitcoinGUI::themeToggled() +{ + clientModel->getOptionsModel()->setCurrentStyle(sSheet == "light" ? "dark" : "light"); +} + void BitcoinGUI::openConfigClicked() { boost::filesystem::path pathConfig = GetConfigFile(); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 5c865286a2..181e5b7c29 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -217,6 +217,8 @@ private slots: /** Show configuration dialog */ void optionsClicked(); + /** Switch the active light/dark theme */ + void themeToggled(); /** Show researcher/beacon configuration dialog */ void researcherClicked(); /** Show about dialog */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index a75287463d..3ceb0cd5b2 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -354,6 +354,11 @@ QString OptionsModel::getCurrentStyle() return walletStylesheet; } +void OptionsModel::setCurrentStyle(QString theme) +{ + setData(QAbstractItemModel::createIndex(WalletStylesheet, 0), theme, Qt::EditRole); +} + QString OptionsModel::getDataDir() { return dataDir; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 691a286d2d..580bfdc777 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -65,6 +65,9 @@ class OptionsModel : public QAbstractListModel QString getCurrentStyle(); QString getDataDir(); + /* Explicit setters */ + void setCurrentStyle(QString theme); + private: int nDisplayUnit; bool fMinimizeToTray; diff --git a/src/qt/res/icons/dark_mode.svg b/src/qt/res/icons/dark_mode.svg new file mode 100644 index 0000000000..0f5ccb338b --- /dev/null +++ b/src/qt/res/icons/dark_mode.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/qt/res/icons/dark_mode_active.svg b/src/qt/res/icons/dark_mode_active.svg new file mode 100644 index 0000000000..b6d2cd443a --- /dev/null +++ b/src/qt/res/icons/dark_mode_active.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/qt/res/icons/light_mode.svg b/src/qt/res/icons/light_mode.svg new file mode 100644 index 0000000000..9d9a47d9a0 --- /dev/null +++ b/src/qt/res/icons/light_mode.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/qt/res/icons/light_mode_active.svg b/src/qt/res/icons/light_mode_active.svg new file mode 100644 index 0000000000..6775d5ea8f --- /dev/null +++ b/src/qt/res/icons/light_mode_active.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 5434de68ee..1c6c763c0e 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -466,18 +466,31 @@ QToolBar#toolbar::separator { border-bottom: 0.065em solid rgb(18, 26, 34); } -#toolbarMenuButton { - image: url(:/icons/menu); +#toolbarMenuButton, +#themeToggleButton { background: none; border: none; - border-radius: 0; - padding: 0; + min-width: 1.1em; + max-width: 1.1em; + min-height: 1.1em; + max-height: 1.1em; + padding: 0.1em; +} + +#toolbarMenuButton:hover, +#toolbarMenuButton:pressed, +#themeToggleButton:hover, +#themeToggleButton::pressed { + background-color: rgb(26, 145, 235); +} + +#toolbarMenuButton { + image: url(:/icons/menu); } #toolbarMenuButton:hover, #toolbarMenuButton:pressed { image: url(:/icons/menu_active); - background-color: rgb(26, 145, 235); } #toolbarMenuButton::menu-indicator { @@ -485,6 +498,15 @@ QToolBar#toolbar::separator { height: 0; } +#themeToggleButton { + image: url(:/icons/light_mode); +} + +#themeToggleButton:hover, +#themeToggleButton:pressed { + image: url(:/icons/light_mode_active); +} + #toolbarLogoLabel { image: url(:/images/gridcoin); min-width: 2.75em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 36cc683abc..66c38a7ba4 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -477,18 +477,31 @@ QToolBar#toolbar::separator { border-bottom: 0.065em solid rgb(74, 26, 117); } -#toolbarMenuButton { - image: url(:/icons/menu); +#toolbarMenuButton, +#themeToggleButton { background: none; border: none; - border-radius: 0; - padding: 0; + min-width: 1.1em; + max-width: 1.1em; + min-height: 1.1em; + max-height: 1.1em; + padding: 0.1em; +} + +#toolbarMenuButton:hover, +#toolbarMenuButton:pressed, +#themeToggleButton:hover, +#themeToggleButton:pressed { + background: rgb(109, 25, 186); +} + +#toolbarMenuButton { + image: url(:/icons/menu); } #toolbarMenuButton:hover, #toolbarMenuButton:pressed { image: url(:/icons/menu_active); - background: rgb(109, 25, 186); } #toolbarMenuButton::menu-indicator { @@ -496,6 +509,15 @@ QToolBar#toolbar::separator { height: 0; } +#themeToggleButton { + image: url(:/icons/dark_mode); +} + +#themeToggleButton:hover, +#themeToggleButton:pressed { + image: url(:/icons/dark_mode_active); +} + #toolbarLogoLabel { image: url(:/images/gridcoin); min-width: 2.75em; From 8af5d52f4155badf5d29f8229da08ad0235b77f9 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 2 May 2021 01:42:02 -0400 Subject: [PATCH 083/280] Increment version to 5.3.1.3. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6c25599089..8b583a41ff 100755 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 3) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 2) +define(_CLIENT_VERSION_BUILD, 3) define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS,[The %s developers]) From f37fdff8472ee9ff9ecde58fb7f9ef4004f232b6 Mon Sep 17 00:00:00 2001 From: sweede-se <79360493+sweede-se@users.noreply.github.com> Date: Sun, 2 May 2021 21:17:06 +0200 Subject: [PATCH 084/280] Wallet: Update addnodes --- src/init.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 8fe2b5e6d2..9599ef72e8 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -191,11 +191,9 @@ static void CreateNewConfigFile() myConfig << "addnode=addnode-us-central.cycy.me\n" << "addnode=ec2-3-81-39-58.compute-1.amazonaws.com\n" - << "addnode=grcnode.thefoxie.eu\n" << "addnode=gridcoin.ddns.net\n" << "addnode=seeds.gridcoin.ifoggz-network.xyz\n" << "addnode=seed.gridcoin.pl\n" - << "addnode=swe.tplinkdns.com\n" << "addnode=www.grcpool.com\n"; } From f7d67ed73387ae15d53099d873c3f93a518371a4 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 2 May 2021 22:23:44 +0300 Subject: [PATCH 085/280] depends: change boost mirror --- depends/packages/boost.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 118981c1f3..991410de95 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,7 +1,7 @@ package=boost GCCFLAGS?= $(package)_version=1_73_0 -$(package)_download_path=https://dl.bintray.com/boostorg/release/1.73.0/source/ +$(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/ $(package)_file_name=$(package)_$($(package)_version).tar.bz2 $(package)_sha256_hash=4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402 $(package)_dependencies=zlib From 3189fa4e1c2dba6485afb0eb84c2a80f0b545949 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Thu, 29 Apr 2021 21:40:36 +0300 Subject: [PATCH 086/280] refactor: remove nNewIndex(2) --- src/main.cpp | 4 ---- src/main.h | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3926025b36..2cc117d202 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -125,8 +125,6 @@ std::string msMiningErrorsExcluded; //When syncing, we grandfather block rejection rules up to this block, as rules became stricter over time and fields changed int nGrandfather = 1034700; -int nNewIndex = 271625; -int nNewIndex2 = 364500; int64_t nGenesisSupply = 340569880; @@ -2763,8 +2761,6 @@ bool LoadBlockIndex(bool fAllowNew) nStakeMinAge = 1 * 60 * 60; // test net min age is 1 hour nCoinbaseMaturity = 10; // test maturity is 10 blocks nGrandfather = 196550; - nNewIndex = 10; - nNewIndex2 = 36500; //1-24-2016 MAX_OUTBOUND_CONNECTIONS = (int)GetArg("-maxoutboundconnections", 8); } diff --git a/src/main.h b/src/main.h index 84226720e5..fccabcd569 100644 --- a/src/main.h +++ b/src/main.h @@ -108,8 +108,6 @@ extern std::string msMiningErrorsIncluded; extern std::string msMiningErrorsExcluded; extern int nGrandfather; -extern int nNewIndex; -extern int nNewIndex2; class GlobalStatus { @@ -943,7 +941,7 @@ class CDiskBlockIndex : public CBlockIndex uint32_t is_superblock = this->IsSuperblock(); uint32_t is_contract = this->IsContract(); - if (this->nHeight > nNewIndex2) { + if (IsResearchAgeEnabled(this->nHeight)) { READWRITE(is_superblock); READWRITE(is_contract); From 77899901dadb079ded22f59dc1c26643783f1712 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Thu, 29 Apr 2021 21:46:00 +0300 Subject: [PATCH 087/280] refactor: remove dropmessagestest --- src/main.cpp | 6 ------ src/net.h | 7 ------- 2 files changed, 13 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2cc117d202..30e1bf1d7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3108,12 +3108,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint(BCLog::LogFlags::NOISY, "received: %s from %s (%" PRIszu " bytes)", strCommand, pfrom->addrName, vRecv.size()); - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - LogPrintf("dropmessagestest DROPPING RECV MESSAGE"); - return true; - } - if (strCommand == "aries") { // Each connection can only send one version message diff --git a/src/net.h b/src/net.h index 5b405d4f30..38805107e6 100644 --- a/src/net.h +++ b/src/net.h @@ -477,13 +477,6 @@ class CNode void EndMessage() { - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - LogPrintf("dropmessages DROPPING SEND MESSAGE"); - AbortMessage(); - return; - } - if (ssSend.size() == 0) return; From 19dd3d52e27f992c9b2158399c456751445e0652 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Thu, 29 Apr 2021 22:12:11 +0300 Subject: [PATCH 088/280] refactor: replace pchMessageStart with Params().MessageStart() --- src/addrdb.cpp | 8 ++++---- src/main.cpp | 21 +++++---------------- src/main.h | 3 +-- src/protocol.cpp | 7 ++++--- src/protocol.h | 2 -- 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index c8d9b9f30c..5369827f95 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -6,7 +6,7 @@ #include #include -// #include +#include // #include #include // #include @@ -26,8 +26,8 @@ bool SerializeDB(Stream& stream, const Data& data) // Write and commit header, data try { CHashWriter hasher(SER_DISK, CLIENT_VERSION); - stream << pchMessageStart << data; - hasher << pchMessageStart << data; + stream << Params().MessageStart() << data; + hasher << Params().MessageStart() << data; stream << hasher.GetHash(); } catch (const std::exception& e) { return error("%s: Serialize or I/O error - %s", __func__, e.what()); @@ -85,7 +85,7 @@ bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true) unsigned char pchMsgTmp[4]; verifier >> pchMsgTmp; // ... verify the network matches ours - if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp))) + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) return error("%s: Invalid network magic number", __func__); // de-serialize data diff --git a/src/main.cpp b/src/main.cpp index 30e1bf1d7e..31e7e516e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2753,10 +2753,6 @@ bool LoadBlockIndex(bool fAllowNew) if (fTestNet) { // GLOBAL TESTNET SETTINGS - R HALFORD - pchMessageStart[0] = 0xcd; - pchMessageStart[1] = 0xf2; - pchMessageStart[2] = 0xc0; - pchMessageStart[3] = 0xef; bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 16 bits PoW target limit for testnet nStakeMinAge = 1 * 60 * 60; // test net min age is 1 hour nCoinbaseMaturity = 10; // test maturity is 10 blocks @@ -2972,18 +2968,18 @@ bool LoadExternalBlockFile(FILE* fileIn) nPos = (unsigned int)-1; break; } - void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart)); + void* nFind = memchr(pchData, Params().MessageStart()[0], nRead + 1 - CMessageHeader::MESSAGE_START_SIZE); if (nFind) { - if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0) + if (memcmp(nFind, Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE) == 0) { - nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart); + nPos += ((unsigned char*)nFind - pchData) + CMessageHeader::MESSAGE_START_SIZE; break; } nPos += ((unsigned char*)nFind - pchData) + 1; } else - nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1; + nPos += sizeof(pchData) - CMessageHeader::MESSAGE_START_SIZE + 1; } while(!fRequestShutdown); if (nPos == (unsigned int)-1) break; @@ -3095,13 +3091,6 @@ bool static AlreadyHave(CTxDB& txdb, const CInv& inv) } - - -// The message start string is designed to be unlikely to occur in normal data. -// The characters are rarely used upper ASCII, not valid as UTF-8, and produce -// a large 4-byte int at any alignment. -unsigned char pchMessageStart[4] = { 0x70, 0x35, 0x22, 0x05 }; - bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { RandAddSeedPerfmon(); @@ -3916,7 +3905,7 @@ bool ProcessMessages(CNode* pfrom) it++; // Scan for message start - if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { + if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) { LogPrint(BCLog::LogFlags::NOISY, "PROCESSMESSAGE: INVALID MESSAGESTART"); fOk = false; break; diff --git a/src/main.h b/src/main.h index fccabcd569..fcd2554734 100644 --- a/src/main.h +++ b/src/main.h @@ -87,7 +87,6 @@ extern CBlockIndex* pindexBest; extern const std::string strMessageMagic; extern CCriticalSection cs_setpwalletRegistered; extern std::set setpwalletRegistered; -extern unsigned char pchMessageStart[4]; extern std::map mapOrphanBlocks; // Settings @@ -504,7 +503,7 @@ class CBlock : public CBlockHeader // Write index header unsigned int nSize = GetSerializeSize(fileout, *this); - fileout << pchMessageStart << nSize; + fileout << Params().MessageStart() << nSize; // Write block long fileOutPos = ftell(fileout.Get()); diff --git a/src/protocol.cpp b/src/protocol.cpp index 7db42fc240..e6f281661b 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "netbase.h" #include "protocol.h" #include "util.h" @@ -22,7 +23,7 @@ static const char* ppszTypeName[] = CMessageHeader::CMessageHeader() { - memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memcpy(pchMessageStart, Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE); memset(pchCommand, 0, sizeof(pchCommand)); nMessageSize = -1; memset(pchChecksum, 0, CHECKSUM_SIZE); @@ -30,7 +31,7 @@ CMessageHeader::CMessageHeader() CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) { - memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memcpy(pchMessageStart, Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE); // Copy the command name, zero-padding to COMMAND_SIZE bytes size_t i = 0; @@ -53,7 +54,7 @@ std::string CMessageHeader::GetCommand() const bool CMessageHeader::IsValid() const { // Check start string - if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) + if (memcmp(pchMessageStart, Params().MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) return false; // Check the command string for errors diff --git a/src/protocol.h b/src/protocol.h index 5b7c6c9357..a5508fda68 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -24,8 +24,6 @@ static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) } -extern unsigned char pchMessageStart[4]; - /** Message header. * (4) message start. * (12) command. From ccf377c2ad5235f836ad2d667ede9e921de070c6 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 2 May 2021 17:09:06 +0300 Subject: [PATCH 089/280] devtools: fix circular-dependencies for Gridcoin style includes --- contrib/devtools/circular-dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/devtools/circular-dependencies.py b/contrib/devtools/circular-dependencies.py index bc5f09a3e2..08c64e0791 100755 --- a/contrib/devtools/circular-dependencies.py +++ b/contrib/devtools/circular-dependencies.py @@ -34,7 +34,7 @@ def module_name(path): files = dict() deps = dict() -RE = re.compile("^#include <(.*)>") +RE = re.compile("^#include [<\"](.*)[\">]") # Iterate over files, and create list of modules for arg in sys.argv[1:]: From 6439b2eec600571825c05f882ff1cb190f5c6b63 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 2 May 2021 17:13:16 +0300 Subject: [PATCH 090/280] devtools: fix copyright-headers --- contrib/devtools/copyright_header.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index 9a555c70bb..f596dd1e5b 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -104,6 +104,7 @@ def compile_copyright_regex(copyright_style, year_style, name): r"Intel Corporation ?", r"The Zcash developers", r"Jeremy Rubin", + r"The Gridcoin developers", ] DOMINANT_STYLE_COMPILED = {} @@ -336,7 +337,7 @@ def write_file_lines(filename, file_lines): COPYRIGHT = r'Copyright \(c\)' YEAR = "20[0-9][0-9]" YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR) -HOLDER = 'The Bitcoin Core developers' +HOLDER = 'The Gridcoin developers' UPDATEABLE_LINE_COMPILED = re.compile(' '.join([COPYRIGHT, YEAR_RANGE, HOLDER])) def get_updatable_copyright_line(file_lines): @@ -401,24 +402,24 @@ def exec_update_header_year(base_directory): ################################################################################ UPDATE_USAGE = """ -Updates all the copyright headers of "The Bitcoin Core developers" which were +Updates all the copyright headers of "The Gridcoin developers" which were changed in a year more recent than is listed. For example: -// Copyright (c) - The Bitcoin Core developers +// Copyright (c) - The Gridcoin developers will be updated to: -// Copyright (c) - The Bitcoin Core developers +// Copyright (c) - The Gridcoin developers where is obtained from the 'git log' history. This subcommand also handles copyright headers that have only a single year. In those cases: -// Copyright (c) The Bitcoin Core developers +// Copyright (c) The Gridcoin developers will be updated to: -// Copyright (c) - The Bitcoin Core developers +// Copyright (c) - The Gridcoin developers where the update is appropriate. @@ -451,7 +452,7 @@ def get_header_lines(header, start_year, end_year): return [line + '\n' for line in lines] CPP_HEADER = ''' -// Copyright (c) %s The Bitcoin Core developers +// Copyright (c) %s The Gridcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' @@ -460,7 +461,7 @@ def get_cpp_header_lines_to_insert(start_year, end_year): return reversed(get_header_lines(CPP_HEADER, start_year, end_year)) SCRIPT_HEADER = ''' -# Copyright (c) %s The Bitcoin Core developers +# Copyright (c) %s The Gridcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' @@ -515,7 +516,7 @@ def insert_cpp_header(filename, file_lines, start_year, end_year): def exec_insert_header(filename, style): file_lines = read_file_lines(filename) if file_already_has_core_copyright(file_lines): - sys.exit('*** %s already has a copyright by The Bitcoin Core developers' + sys.exit('*** %s already has a copyright by The Gridcoin developers' % (filename)) start_year, end_year = get_git_change_year_range(filename) if style in ['python', 'shell']: @@ -528,7 +529,7 @@ def exec_insert_header(filename, style): ################################################################################ INSERT_USAGE = """ -Inserts a copyright header for "The Bitcoin Core developers" at the top of the +Inserts a copyright header for "The Gridcoin developers" at the top of the file in either Python or C++ style as determined by the file extension. If the file is a Python file and it has a '#!' starting the first line, the header is inserted in the line below it. @@ -542,7 +543,7 @@ def exec_insert_header(filename, style): "" -If the file already has a copyright for "The Bitcoin Core developers", the +If the file already has a copyright for "The Gridcoin developers", the script will exit. Usage: @@ -576,8 +577,8 @@ def insert_cmd(argv): ################################################################################ USAGE = """ -copyright_header.py - utilities for managing copyright headers of 'The Bitcoin -Core developers' in repository source files. +copyright_header.py - utilities for managing copyright headers of 'The Gridcoin +developers' in repository source files. Usage: $ ./copyright_header From 7fbda0e200e505ad84bd2eea8164f11470fbce0f Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 13 May 2021 22:27:31 -0500 Subject: [PATCH 091/280] Unsquelch CI before_script test stage This was hiding problems with depends downloads. --- ci/test_run_all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test_run_all.sh b/ci/test_run_all.sh index f8a2594b71..a1d4bd1952 100755 --- a/ci/test_run_all.sh +++ b/ci/test_run_all.sh @@ -9,6 +9,6 @@ export LC_ALL=C.UTF-8 set -o errexit; source ./ci/test/00_setup_env.sh set -o errexit; source ./ci/test/03_before_install.sh set -o errexit; source ./ci/test/04_install.sh -set -o errexit; source ./ci/test/05_before_script.sh &> "/dev/null" +set -o errexit; source ./ci/test/05_before_script.sh set -o errexit; source ./ci/test/06_script_a.sh set -o errexit; source ./ci/test/06_script_b.sh From 0f35ce0bd876863f88a9d8b542dc5da634c905df Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 13 May 2021 23:17:17 -0500 Subject: [PATCH 092/280] Install libxkbcommon depends recipe bitcoin/bitcoin@cc25f892d27351e60a8cf7bf5e60b167ebe33201 This is a dependency needed to update the Qt recipe to version 5.12.10. --- depends/packages/libxkbcommon.mk | 32 ++++++++++++++++++++++++++++++++ depends/packages/packages.mk | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 depends/packages/libxkbcommon.mk diff --git a/depends/packages/libxkbcommon.mk b/depends/packages/libxkbcommon.mk new file mode 100644 index 0000000000..8c6c56545f --- /dev/null +++ b/depends/packages/libxkbcommon.mk @@ -0,0 +1,32 @@ +package=libxkbcommon +$(package)_version=0.8.4 +$(package)_download_path=https://xkbcommon.org/download/ +$(package)_file_name=$(package)-$($(package)_version).tar.xz +$(package)_sha256_hash=60ddcff932b7fd352752d51a5c4f04f3d0403230a584df9a2e0d5ed87c486c8b +$(package)_dependencies=libxcb + +define $(package)_set_vars +$(package)_config_opts = --enable-option-checking --disable-dependency-tracking +$(package)_config_opts += --disable-static --disable-docs +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index dcb45a0629..45f23dccbe 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -3,7 +3,7 @@ native_packages := native_ccache qt_packages = qrencode -qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans +qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans libxkbcommon qt_darwin_packages=qt qt_mingw32_packages=qt From 0c2f6e7b02a78321da6ec756dcaf000ea04ef20d Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 13 May 2021 23:20:15 -0500 Subject: [PATCH 093/280] Update depends Qt recipe to version 5.12.10 bitcoin/bitcoin@03e16cb02730a1df5c367b7fd7a81f62a6d1b90d Qt version 5.9 is end-of-life. This updates the Qt depends recipe from Qt 5.9.8 to 5.12.10 (LTS). --- .github/workflows/ci.yml | 2 +- depends/hosts/darwin.mk | 2 +- depends/packages/qt.mk | 74 ++++--- .../patches/qt/drop_lrelease_dependency.patch | 2 +- .../patches/qt/fix_android_jni_static.patch | 2 +- depends/patches/qt/fix_android_pch.patch | 10 + .../patches/qt/fix_android_qmake_conf.patch | 24 +-- depends/patches/qt/fix_bigsur_drawing.patch | 31 +++ depends/patches/qt/fix_configure_mac.patch | 50 ----- depends/patches/qt/fix_lib_paths.patch | 193 ++++++++++++++++++ .../patches/qt/fix_mingw_cross_compile.patch | 25 --- depends/patches/qt/fix_no_printer.patch | 4 +- depends/patches/qt/fix_powerpc_libpng.patch | 23 --- .../qt/fix_qpainter_non_determinism.patch | 63 ------ depends/patches/qt/fix_qt_pkgconfig.patch | 12 +- depends/patches/qt/fix_rcc_determinism.patch | 15 -- depends/patches/qt/fix_riscv64_arch.patch | 14 -- depends/patches/qt/freetype_back_compat.patch | 28 --- depends/patches/qt/no-xlib.patch | 17 +- depends/patches/qt/no_sdk_version_check.patch | 20 ++ .../qt/qtbase-moc-ignore-gcc-macro.patch | 17 ++ depends/patches/qt/xkb-default.patch | 26 --- 22 files changed, 334 insertions(+), 320 deletions(-) create mode 100644 depends/patches/qt/fix_android_pch.patch create mode 100644 depends/patches/qt/fix_bigsur_drawing.patch delete mode 100644 depends/patches/qt/fix_configure_mac.patch create mode 100644 depends/patches/qt/fix_lib_paths.patch delete mode 100644 depends/patches/qt/fix_mingw_cross_compile.patch delete mode 100644 depends/patches/qt/fix_powerpc_libpng.patch delete mode 100644 depends/patches/qt/fix_qpainter_non_determinism.patch delete mode 100644 depends/patches/qt/fix_rcc_determinism.patch delete mode 100644 depends/patches/qt/fix_riscv64_arch.patch delete mode 100644 depends/patches/qt/freetype_back_compat.patch create mode 100644 depends/patches/qt/no_sdk_version_check.patch create mode 100644 depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch delete mode 100644 depends/patches/qt/xkb-default.patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01c7f38b28..1672ad999f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: script-id: native - name: x86_64 Linux [GOAL install] [GUI] [xenial] [no depends] script-id: native_old - - name: macOS 10.11 [GOAL deploy] [GUI] [no tests] [focal] + - name: macOS 10.14 [GOAL deploy] [GUI] [no tests] [focal] script-id: mac env: FILE_ENV: ./ci/test/00_setup_env_${{ matrix.script-id }}.sh diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index c9ee46294d..646e97837a 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -1,4 +1,4 @@ -OSX_MIN_VERSION=10.11 +OSX_MIN_VERSION=10.14 OSX_SDK_VERSION=10.15.1 XCODE_VERSION=11.3.1 XCODE_BUILD_ID=11C505 diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 39f501bf5d..80faa16abf 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,31 +1,30 @@ PACKAGE=qt -$(package)_version=5.9.8 -$(package)_download_path=https://download.qt.io/official_releases/qt/5.9/$($(package)_version)/submodules -$(package)_suffix=opensource-src-$($(package)_version).tar.xz +$(package)_version=5.12.10 +$(package)_download_path=https://download.qt.io/official_releases/qt/5.12/$($(package)_version)/submodules +$(package)_suffix=everywhere-src-$($(package)_version).tar.xz $(package)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=9b9dec1f67df1f94bce2955c5604de992d529dde72050239154c56352da0907d -$(package)_dependencies=zlib -$(package)_linux_dependencies=freetype fontconfig libxcb +$(package)_sha256_hash=8088f174e6d28e779516c083b6087b6a9e3c8322b4bc161fd1b54195e3c86940 +$(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon $(package)_qt_libs=corelib network widgets gui plugins testlib concurrent -$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch -$(package)_patches+= fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch +$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no-xlib.patch $(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch -$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch fix_powerpc_libpng.patch -$(package)_patches+= fix_mingw_cross_compile.patch fix_qpainter_non_determinism.patch subdirs.pro +$(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch +$(package)_patches+= fix_lib_paths.patch fix_android_pch.patch +$(package)_patches+= fix_bigsur_drawing.patch qtbase-moc-ignore-gcc-macro.patch subdirs.pro $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=fb5a47799754af73d3bf501fe513342cfe2fc37f64e80df5533f6110e804220c +$(package)_qttranslations_sha256_hash=e1de58ed108b7e0a138815ea60fd46a2c4e1fc31396a707e5630e92de79c53de $(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=a97556eb7b2f30252cdd8a598c396cfce2b2f79d2bae883af6d3b26a2cdcc63c +$(package)_qttools_sha256_hash=b0cfa6e7aac41b7c61fc59acc04843d7a98f9e1840370611751bcfc1834a636c # Gridcoin uses charts for the voting features: $(package)_qtcharts_file_name=qtcharts-$($(package)_suffix) -$(package)_qtcharts_sha256_hash=a75f89c0081af9635b50cab335a4871d476b36abc8a11dc4f24724bd3cf42437 +$(package)_qtcharts_sha256_hash=ac297e27844d3193205db02a22d68a9af7d79f416891617f9389ba4d29aacd32 # Gridcoin displays SVG images in the GUI: $(package)_qtsvg_file_name=qtsvg-$($(package)_suffix) -$(package)_qtsvg_sha256_hash=c15d0c4ed93b168a6473749dd70cb04b3cc8e8af584447f2701be4cf2f11c5db +$(package)_qtsvg_sha256_hash=b5fff4cfe158e17cb3f182c521aa6385f56e158b816d6dff1de915213cfa334b $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) @@ -34,7 +33,9 @@ $(package)_extra_sources += $($(package)_qtsvg) define $(package)_set_vars $(package)_config_opts_release = -release +$(package)_config_opts_release += -silent $(package)_config_opts_debug = -debug +$(package)_config_opts_debug += -optimized-tools $(package)_config_opts += -bindir $(build_prefix)/bin $(package)_config_opts += -c++std c++1z $(package)_config_opts += -confirm-license @@ -58,7 +59,6 @@ $(package)_config_opts += -no-mtdev $(package)_config_opts += -no-openssl $(package)_config_opts += -no-openvg $(package)_config_opts += -no-reduce-relocations -$(package)_config_opts += -no-qml-debug $(package)_config_opts += -no-sctp $(package)_config_opts += -no-securetransport $(package)_config_opts += -no-sql-db2 @@ -72,19 +72,16 @@ $(package)_config_opts += -no-sql-sqlite $(package)_config_opts += -no-sql-sqlite2 $(package)_config_opts += -no-system-proxies $(package)_config_opts += -no-use-gold-linker -$(package)_config_opts += -no-xinput2 $(package)_config_opts += -nomake examples $(package)_config_opts += -nomake tests $(package)_config_opts += -opensource -$(package)_config_opts += -optimized-tools $(package)_config_opts += -pkg-config $(package)_config_opts += -prefix $(host_prefix) $(package)_config_opts += -qt-libpng $(package)_config_opts += -qt-pcre $(package)_config_opts += -qt-harfbuzz -$(package)_config_opts += -system-zlib +$(package)_config_opts += -qt-zlib $(package)_config_opts += -static -$(package)_config_opts += -silent $(package)_config_opts += -v $(package)_config_opts += -no-feature-bearermanagement $(package)_config_opts += -no-feature-colordialog @@ -105,11 +102,10 @@ $(package)_config_opts += -no-feature-printdialog $(package)_config_opts += -no-feature-printer $(package)_config_opts += -no-feature-printpreviewdialog $(package)_config_opts += -no-feature-printpreviewwidget -# Gridcoin uses QRegularExpression: -#$(package)_config_opts += -no-feature-regularexpression $(package)_config_opts += -no-feature-sessionmanager $(package)_config_opts += -no-feature-socks5 $(package)_config_opts += -no-feature-sql +$(package)_config_opts += -no-feature-sqlmodel $(package)_config_opts += -no-feature-statemachine $(package)_config_opts += -no-feature-syntaxhighlighter $(package)_config_opts += -no-feature-textbrowser @@ -129,6 +125,8 @@ $(package)_config_opts += -no-feature-xml $(package)_config_opts_darwin = -no-dbus $(package)_config_opts_darwin += -no-opengl $(package)_config_opts_darwin += -pch +$(package)_config_opts_darwin += -no-feature-corewlan +$(package)_config_opts_darwin += -device-option QMAKE_MACOSX_DEPLOYMENT_TARGET=$(OSX_MIN_VERSION) ifneq ($(build_os),darwin) $(package)_config_opts_darwin += -xplatform macx-clang-linux @@ -143,13 +141,13 @@ endif # for macOS on Apple Silicon (ARM) see https://bugreports.qt.io/browse/QTBUG-85279 $(package)_config_opts_arm_darwin += -device-option QMAKE_APPLE_DEVICE_ARCHS=arm64 -$(package)_config_opts_linux = -qt-xkbcommon-x11 -$(package)_config_opts_linux += -qt-xcb +$(package)_config_opts_linux = -qt-xcb $(package)_config_opts_linux += -no-xcb-xlib $(package)_config_opts_linux += -no-feature-xlib $(package)_config_opts_linux += -system-freetype $(package)_config_opts_linux += -fontconfig $(package)_config_opts_linux += -no-opengl +$(package)_config_opts_linux += -no-feature-vulkan $(package)_config_opts_linux += -dbus-runtime $(package)_config_opts_arm_linux += -platform linux-g++ -xplatform bitcoin-linux-g++ $(package)_config_opts_i686_linux = -xplatform linux-g++-32 @@ -163,6 +161,9 @@ $(package)_config_opts_s390x_linux = -platform linux-g++ -xplatform bitcoin-linu $(package)_config_opts_mingw32 = -no-opengl $(package)_config_opts_mingw32 += -no-dbus $(package)_config_opts_mingw32 += -xplatform win32-g++ +$(package)_config_opts_mingw32 += "QMAKE_CFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" +$(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" +$(package)_config_opts_mingw32 += "QMAKE_LFLAGS = '$($(package)_ldflags)'" $(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_mingw32 += -pch @@ -181,13 +182,13 @@ $(package)_config_opts_android += -no-fontconfig $(package)_config_opts_android += -L $(host_prefix)/lib $(package)_config_opts_android += -I $(host_prefix)/include $(package)_config_opts_android += -pch +$(package)_config_opts_android += -no-feature-vulkan $(package)_config_opts_aarch64_android += -android-arch arm64-v8a $(package)_config_opts_armv7a_android += -android-arch armeabi-v7a $(package)_config_opts_x86_64_android += -android-arch x86_64 $(package)_config_opts_i686_android += -android-arch i686 -$(package)_build_env = QT_RCC_TEST=1 $(package)_build_env += QT_RCC_SOURCE_DATE_OVERRIDE=1 endef @@ -247,21 +248,18 @@ endef # CROSS_LIBRARY_PATH. See #15277. define $(package)_preprocess_cmds rm -f $(BASEDIR)/.qmake.stash && \ - patch -p1 -i $($(package)_patch_dir)/freetype_back_compat.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_powerpc_libpng.patch && \ patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \ patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_configure_mac.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_rcc_determinism.patch && \ - patch -p1 -i $($(package)_patch_dir)/xkb-default.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_android_qmake_conf.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_android_jni_static.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_riscv64_arch.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_android_pch.patch && \ patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_mingw_cross_compile.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_qpainter_non_determinism.patch &&\ + patch -p1 -i $($(package)_patch_dir)/no_sdk_version_check.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \ + patch -p1 -i $($(package)_patch_dir)/fix_bigsur_drawing.patch && \ + patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.patch && \ cp $($(package)_patch_dir)/subdirs.pro subdirs.pro && \ sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ mkdir -p qtbase/mkspecs/macx-clang-linux &&\ @@ -272,11 +270,8 @@ define $(package)_preprocess_cmds echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ - sed -i.old "s|QMAKE_CFLAGS += |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ - sed -i.old "s|QMAKE_CXXFLAGS += |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ - sed -i.old "0,/^QMAKE_LFLAGS_/s|^QMAKE_LFLAGS_|!host_build: QMAKE_LFLAGS = $($(package)_ldflags)\n&|" qtbase/mkspecs/win32-g++/qmake.conf && \ - sed -i.old "s|QMAKE_CC = clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \ - sed -i.old "s|QMAKE_CXX = clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \ + sed -i.old "s|QMAKE_CC = \$$$$\$$$${CROSS_COMPILE}clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \ + sed -i.old "s|QMAKE_CXX = \$$$$\$$$${CROSS_COMPILE}clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \ sed -i.old "s/LIBRARY_PATH/(CROSS_)?\0/g" qtbase/mkspecs/features/toolchain.prf endef @@ -286,8 +281,6 @@ define $(package)_config_cmds export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \ cd qtbase && \ ./configure -top-level $($(package)_config_opts) && \ - echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \ - echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \ cd .. && \ qtbase/bin/qmake -o qtbase/Makefile qtbase/qtbase.pro && \ $(MAKE) -C qtbase sub-src-clean && \ @@ -295,6 +288,7 @@ define $(package)_config_cmds qtbase/bin/qmake -o qttranslations/translations/Makefile qttranslations/translations/translations.pro && \ qtbase/bin/qmake -o qttools/src/linguist/lrelease/Makefile qttools/src/linguist/lrelease/lrelease.pro && \ qtbase/bin/qmake -o qttools/src/linguist/lupdate/Makefile qttools/src/linguist/lupdate/lupdate.pro && \ + qtbase/bin/qmake -o qttools/src/linguist/lconvert/Makefile qttools/src/linguist/lconvert/lconvert.pro && \ qtbase/bin/qmake -o qtcharts/src/charts/Makefile qtcharts/src/charts/charts.pro && \ qtbase/bin/qmake -o qtsvg/src/Makefile qtsvg/src/src.pro endef @@ -304,6 +298,7 @@ define $(package)_build_cmds $(MAKE) -C qtbase/src $(addprefix sub-,$($(package)_qt_libs)) && \ $(MAKE) -C qttools/src/linguist/lrelease && \ $(MAKE) -C qttools/src/linguist/lupdate && \ + $(MAKE) -C qttools/src/linguist/lconvert && \ $(MAKE) -C qttranslations && \ $(MAKE) -C qtcharts/src/charts && \ $(MAKE) -C qtsvg/src \ @@ -315,6 +310,7 @@ define $(package)_stage_cmds $(MAKE) -C qtbase/src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && \ $(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \ $(MAKE) -C qttools/src/linguist/lupdate INSTALL_ROOT=$($(package)_staging_dir) install_target && \ + $(MAKE) -C qttools/src/linguist/lconvert INSTALL_ROOT=$($(package)_staging_dir) install_target && \ $(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets && \ $(MAKE) -C qtcharts/src/charts INSTALL_ROOT=$($(package)_staging_dir) install && \ $(MAKE) -C qtsvg/src INSTALL_ROOT=$($(package)_staging_dir) install \ diff --git a/depends/patches/qt/drop_lrelease_dependency.patch b/depends/patches/qt/drop_lrelease_dependency.patch index f6b2c9fc80..9b918af77c 100644 --- a/depends/patches/qt/drop_lrelease_dependency.patch +++ b/depends/patches/qt/drop_lrelease_dependency.patch @@ -14,7 +14,7 @@ diff --git a/qttranslations/translations/translations.pro b/qttranslations/trans index 694544c..eff339d 100644 --- a/qttranslations/translations/translations.pro +++ b/qttranslations/translations/translations.pro -@@ -109,3 +109,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} +@@ -107,3 +107,2 @@ updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} silent:updateqm.commands = @echo lrelease ${QMAKE_FILE_IN} && $$updateqm.commands -updateqm.depends = $$LRELEASE_EXE updateqm.name = LRELEASE ${QMAKE_FILE_IN} diff --git a/depends/patches/qt/fix_android_jni_static.patch b/depends/patches/qt/fix_android_jni_static.patch index 2f6ff00f40..f891da6ddf 100644 --- a/depends/patches/qt/fix_android_jni_static.patch +++ b/depends/patches/qt/fix_android_jni_static.patch @@ -1,6 +1,6 @@ --- old/qtbase/src/plugins/platforms/android/androidjnimain.cpp +++ new/qtbase/src/plugins/platforms/android/androidjnimain.cpp -@@ -890,6 +890,14 @@ +@@ -897,6 +897,14 @@ __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } diff --git a/depends/patches/qt/fix_android_pch.patch b/depends/patches/qt/fix_android_pch.patch new file mode 100644 index 0000000000..bed6e4bb63 --- /dev/null +++ b/depends/patches/qt/fix_android_pch.patch @@ -0,0 +1,10 @@ +--- old/qtbase/mkspecs/common/android-base-head.conf ++++ new/qtbase/mkspecs/common/android-base-head.conf +@@ -73,6 +73,6 @@ CROSS_COMPILE = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLS_PREFIX- + QMAKE_PCH_OUTPUT_EXT = .gch + + QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} +-QMAKE_CFLAGS_USE_PRECOMPILE = -include ${QMAKE_PCH_OUTPUT_BASE} ++QMAKE_CFLAGS_USE_PRECOMPILE = -include-pch ${QMAKE_PCH_OUTPUT} + QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} + QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE diff --git a/depends/patches/qt/fix_android_qmake_conf.patch b/depends/patches/qt/fix_android_qmake_conf.patch index 13bfff9776..3a8753fd1d 100644 --- a/depends/patches/qt/fix_android_qmake_conf.patch +++ b/depends/patches/qt/fix_android_qmake_conf.patch @@ -1,20 +1,10 @@ --- old/qtbase/mkspecs/android-clang/qmake.conf +++ new/qtbase/mkspecs/android-clang/qmake.conf -@@ -30,7 +30,7 @@ - QMAKE_CFLAGS += -target mips64el-none-linux-android +@@ -47,7 +47,7 @@ ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so + ANDROID_USE_LLVM = true - QMAKE_CFLAGS += -gcc-toolchain $$NDK_TOOLCHAIN_PATH --QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -+QMAKE_LINK = $$QMAKE_CXX $$QMAKE_CFLAGS -Wl,--exclude-libs,libgcc.a -nostdlib++ - QMAKE_CFLAGS += -DANDROID_HAS_WSTRING --sysroot=$$NDK_ROOT/sysroot \ - -isystem $$NDK_ROOT/sysroot/usr/include/$$NDK_TOOLS_PREFIX \ - -isystem $$NDK_ROOT/sources/cxx-stl/llvm-libc++/include \ -@@ -40,7 +40,7 @@ - ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH - - ANDROID_STDCPP_PATH = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++_shared.so --ANDROID_CXX_STL_LIBS = -lc++ -+ANDROID_CXX_STL_LIBS = -lc++_shared - - QMAKE_ARM_CFLAGS_RELEASE = -Oz - QMAKE_ARM_CFLAGS_RELEASE_WITH_DEBUGINFO = -g -Oz + exists($$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so): \ +- ANDROID_CXX_STL_LIBS = -lc++ ++ ANDROID_CXX_STL_LIBS = -lc++_shared + else: \ + ANDROID_CXX_STL_LIBS = $$ANDROID_SOURCES_CXX_STL_LIBDIR/libc++.so.$$replace(ANDROID_PLATFORM, "android-", "") diff --git a/depends/patches/qt/fix_bigsur_drawing.patch b/depends/patches/qt/fix_bigsur_drawing.patch new file mode 100644 index 0000000000..98c0c6be30 --- /dev/null +++ b/depends/patches/qt/fix_bigsur_drawing.patch @@ -0,0 +1,31 @@ +Fix GUI stuck on Big Sur + +See: + - https://github.com/bitcoin-core/gui/issues/249 + - https://github.com/bitcoin/bitcoin/pull/21495 + - https://bugreports.qt.io/browse/QTBUG-87014 + +We should be able to drop this once we are using one of the following versions: + - Qt 5.12.11 or later, see upstream commit: c5d904639dbd690a36306e2b455610029704d821 + - Qt 5.15.3 or later, see upstream commit: 2cae34354bd41ae286258c7a6b3653b746e786ae + +--- a/qtbase/src/plugins/platforms/cocoa/qnsview_drawing.mm ++++ b/qtbase/src/plugins/platforms/cocoa/qnsview_drawing.mm +@@ -95,8 +95,15 @@ + // by AppKit at a point where we've already set up other parts of the platform plugin + // based on the presence of layers or not. Once we've rewritten these parts to support + // dynamically picking up layer enablement we can let AppKit do its thing. +- return QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave +- && QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave; ++ ++ if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSBigSur) ++ return true; // Big Sur always enables layer-backing, regardless of SDK ++ ++ if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave ++ && QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave) ++ return true; // Mojave and Catalina enable layers based on the app's SDK ++ ++ return false; // Prior versions needed explicitly enabled layer backing + } + + - (BOOL)layerExplicitlyRequested diff --git a/depends/patches/qt/fix_configure_mac.patch b/depends/patches/qt/fix_configure_mac.patch deleted file mode 100644 index 0d7dd647de..0000000000 --- a/depends/patches/qt/fix_configure_mac.patch +++ /dev/null @@ -1,50 +0,0 @@ ---- old/qtbase/mkspecs/features/mac/sdk.prf 2018-02-08 10:24:48.000000000 -0800 -+++ new/qtbase/mkspecs/features/mac/sdk.prf 2018-03-23 10:38:56.000000000 -0700 -@@ -8,21 +8,21 @@ - defineReplace(xcodeSDKInfo) { - info = $$1 - equals(info, "Path"): \ -- info = --show-sdk-path -+ infoarg = --show-sdk-path - equals(info, "PlatformPath"): \ -- info = --show-sdk-platform-path -+ infoarg = --show-sdk-platform-path - equals(info, "SDKVersion"): \ -- info = --show-sdk-version -+ infoarg = --show-sdk-version - sdk = $$2 - isEmpty(sdk): \ - sdk = $$QMAKE_MAC_SDK - - isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}) { -- QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$info 2>/dev/null") -+ QMAKE_MAC_SDK.$${sdk}.$${info} = $$system("/usr/bin/xcrun --sdk $$sdk $$infoarg 2>/dev/null") - # --show-sdk-platform-path won't work for Command Line Tools; this is fine - # only used by the XCTest backend to testlib -- isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(info, "--show-sdk-platform-path")): \ -- error("Could not resolve SDK $$info for \'$$sdk\'") -+ isEmpty(QMAKE_MAC_SDK.$${sdk}.$${info}):if(!isEmpty(QMAKE_XCODEBUILD_PATH)|!equals(infoarg, "--show-sdk-platform-path")): \ -+ error("Could not resolve SDK $$info for \'$$sdk\' using $$infoarg") - cache(QMAKE_MAC_SDK.$${sdk}.$${info}, set stash, QMAKE_MAC_SDK.$${sdk}.$${info}) - } - ---- old/qtbase/configure 2018-02-08 10:24:48.000000000 -0800 -+++ new/qtbase/configure 2018-03-23 05:42:29.000000000 -0700 -@@ -232,8 +232,13 @@ - - sdk=$(getSingleQMakeVariable "QMAKE_MAC_SDK" "$1") - if [ -z "$sdk" ]; then echo "QMAKE_MAC_SDK must be set when building on Mac" >&2; exit 1; fi -- sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null) -- if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi -+ sysroot=$(getSingleQMakeVariable "QMAKE_MAC_SDK_PATH" "$1") -+ -+ echo "sysroot pre-configured as $sysroot"; -+ if [ -z "$sysroot" ]; then -+ sysroot=$(/usr/bin/xcrun --sdk $sdk --show-sdk-path 2>/dev/null) -+ if [ -z "$sysroot" ]; then echo "Failed to resolve SDK path for '$sdk'" >&2; exit 1; fi -+ fi - - case "$sdk" in - macosx*) - - diff --git a/depends/patches/qt/fix_lib_paths.patch b/depends/patches/qt/fix_lib_paths.patch new file mode 100644 index 0000000000..d1a15373f4 --- /dev/null +++ b/depends/patches/qt/fix_lib_paths.patch @@ -0,0 +1,193 @@ +--- old/qtbase/mkspecs/common/mac.conf ++++ new/qtbase/mkspecs/common/mac.conf +@@ -14,7 +14,6 @@ + + QMAKE_RESOURCE = /Developer/Tools/Rez + QMAKE_EXTENSION_SHLIB = dylib +-QMAKE_EXTENSIONS_AUX_SHLIB = tbd + QMAKE_LIBDIR = + + # sdk.prf will prefix the proper SDK sysroot + +--- old/qtbase/mkspecs/features/qmake_use.prf ++++ new/qtbase/mkspecs/features/qmake_use.prf +@@ -22,6 +22,8 @@ + !defined(QMAKE_LIBS_$$nu, var): \ + error("Library '$$lower($$replace(nu, _, -))' is not defined.") + ++ QMAKE_LIBDIR += $$eval(QMAKE_LIBDIR_$$nu) ++ + debug: \ + LIBS$${suffix} += $$eval(QMAKE_LIBS_$${nu}_DEBUG) $$eval(QMAKE_LIBS_$$nu) + else: \ + +--- old/qtbase/mkspecs/features/qt_configure.prf ++++ new/qtbase/mkspecs/features/qt_configure.prf +@@ -526,98 +526,23 @@ + return($$sysrootified) + } + +-# libs-var, libs, in-paths, out-paths-var ++# libs-var, libs, in-paths + defineTest(qtConfResolveLibs) { +- ret = true +- paths = $$3 +- out = +- copy = false +- for (l, 2) { +- $$copy { +- copy = false +- out += $$l +- } else: equals(l, "-s") { +- # em++ flag to link libraries from emscripten-ports; passed on literally. +- copy = true +- out += $$l +- } else: contains(l, "^-L.*") { +- lp = $$replace(l, "^-L", ) +- gcc: lp = $$qtGccSysrootifiedPath($$lp) +- !exists($$lp/.) { +- qtLog("Library path $$val_escape(lp) is invalid.") +- ret = false +- } else { +- paths += $$lp +- } +- } else: contains(l, "^-l.*") { +- lib = $$replace(l, "^-l", ) +- lcan = +- integrity:contains(lib, "^.*\\.a") { +- # INTEGRITY compiler searches for exact filename +- # if -l argument has .a suffix +- lcan += $${lib} +- } else: contains(lib, "^:.*") { +- # Use exact filename when -l:filename syntax is used. +- lib ~= s/^:// +- lcan += $${lib} +- } else: unix { +- # Under UNIX, we look for actual shared libraries, in addition +- # to static ones. +- shexts = $$QMAKE_EXTENSION_SHLIB $$QMAKE_EXTENSIONS_AUX_SHLIB +- for (ext, shexts) { +- lcan += $${QMAKE_PREFIX_SHLIB}$${lib}.$${ext} +- } +- lcan += \ +- $${QMAKE_PREFIX_STATICLIB}$${lib}.$${QMAKE_EXTENSION_STATICLIB} +- } else { +- # Under Windows, we look only for static libraries, as even for DLLs +- # one actually links against a static import library. +- mingw { +- lcan += \ +- # MinGW supports UNIX-style library naming in addition to +- # the MSVC style. +- lib$${lib}.dll.a lib$${lib}.a \ +- # Fun fact: prefix-less libraries are also supported. +- $${lib}.dll.a $${lib}.a +- } +- lcan += $${lib}.lib +- } +- l = $$qtConfFindInPathList($$lcan, $$paths $$EXTRA_LIBDIR $$QMAKE_DEFAULT_LIBDIRS) +- isEmpty(l) { +- qtLog("None of [$$val_escape(lcan)] found in [$$val_escape(paths)] and global paths.") +- ret = false +- } else { +- out += $$l +- } +- } else { +- out += $$l +- } +- } +- $$1 = $$out ++ for (path, 3): \ ++ pre_lflags += -L$$path ++ $$1 = $$pre_lflags $$2 + export($$1) +- !isEmpty(4) { +- $$4 = $$paths +- export($$4) +- } +- return($$ret) +-} +- +-# source-var +-defineTest(qtConfResolveAllLibs) { +- ret = true +- !qtConfResolveLibs($${1}.libs, $$eval($${1}.libs), , $${1}.libdirs): \ +- ret = false +- for (b, $${1}.builds._KEYS_): \ +- !qtConfResolveLibs($${1}.builds.$${b}, $$eval($${1}.builds.$${b}), $$eval($${1}.libdirs), ): \ +- ret = false +- return($$ret) ++ return(true) + } + + # libs-var, in-paths, libs + defineTest(qtConfResolvePathLibs) { + ret = true +- gcc: 2 = $$qtGccSysrootifiedPaths($$2) +- for (libdir, 2) { ++ gcc: \ ++ local_paths = $$qtGccSysrootifiedPaths($$2) ++ else: \ ++ local_paths = $$2 ++ for (libdir, local_paths) { + !exists($$libdir/.) { + qtLog("Library path $$val_escape(libdir) is invalid.") + ret = false +@@ -667,8 +592,11 @@ + # includes-var, in-paths, test-object-var + defineTest(qtConfResolvePathIncs) { + ret = true +- gcc: 2 = $$qtGccSysrootifiedPaths($$2) +- for (incdir, 2) { ++ gcc: \ ++ local_paths = $$qtGccSysrootifiedPaths($$2) ++ else: \ ++ local_paths = $$2 ++ for (incdir, local_paths) { + !exists($$incdir/.) { + qtLog("Include path $$val_escape(incdir) is invalid.") + ret = false +@@ -727,6 +655,7 @@ + vars += $$eval(config.commandline.rev_assignments.$${iv}) + defined(config.input.$${iv}, var) { + eval($${1}.builds.$${b} = $$eval(config.input.$${iv})) ++ export($${1}.builds.$${b}) + $${1}.builds._KEYS_ *= $${b} + any = true + } else { +@@ -741,11 +670,14 @@ + export($${1}.builds._KEYS_) + # we also reset the generic libs, to avoid surprises. + $${1}.libs = ++ export($${1}.libs) + } + + # direct libs. overwrites inline libs. +- defined(config.input.$${input}.libs, var): \ ++ defined(config.input.$${input}.libs, var) { + eval($${1}.libs = $$eval(config.input.$${input}.libs)) ++ export($${1}.libs) ++ } + + includes = $$eval(config.input.$${input}.incdir) + +@@ -754,6 +686,7 @@ + !isEmpty(prefix) { + includes += $$prefix/include + $${1}.libs = -L$$prefix/lib $$eval($${1}.libs) ++ export($${1}.libs) + } + + libdir = $$eval(config.input.$${input}.libdir) +@@ -762,11 +695,9 @@ + for (ld, libdir): \ + libs += -L$$ld + $${1}.libs = $$libs $$eval($${1}.libs) ++ export($${1}.libs) + } + +- !qtConfResolveAllLibs($$1): \ +- return(false) +- + !qtConfResolvePathIncs($${1}.includedir, $$includes, $$2): \ + return(false) + diff --git a/depends/patches/qt/fix_mingw_cross_compile.patch b/depends/patches/qt/fix_mingw_cross_compile.patch deleted file mode 100644 index 67f76f1d85..0000000000 --- a/depends/patches/qt/fix_mingw_cross_compile.patch +++ /dev/null @@ -1,25 +0,0 @@ -commit 5a992a549adfe5a587bbcd6cd2b2cee47d236e27 -Author: fanquake -Date: Fri Sep 4 08:13:44 2020 +0800 - - Work around broken mingw cross-compilation - - See upstream issues: - https://bugreports.qt.io/browse/QTBUG-63637 - https://bugreports.qt.io/browse/QTBUG-63659 - https://codereview.qt-project.org/q/8bebded9 - - We should be able to drop this once we are building qt 5.10.1 or later. - - Added in #12971. - -diff --git a/qtbase/mkspecs/win32-g++/qmake.conf b/qtbase/mkspecs/win32-g++/qmake.conf -index e071a0d1..ad229b10 100644 ---- a/qtbase/mkspecs/win32-g++/qmake.conf -+++ b/qtbase/mkspecs/win32-g++/qmake.conf -@@ -87,3 +87,5 @@ QMAKE_NM = $${CROSS_COMPILE}nm -P - include(../common/angle.conf) - - load(qt_config) -+QMAKE_LINK_OBJECT_MAX = 10 -+QMAKE_LINK_OBJECT_SCRIPT = object_script diff --git a/depends/patches/qt/fix_no_printer.patch b/depends/patches/qt/fix_no_printer.patch index f868ca2577..1372356138 100644 --- a/depends/patches/qt/fix_no_printer.patch +++ b/depends/patches/qt/fix_no_printer.patch @@ -10,10 +10,10 @@ --- x/qtbase/src/plugins/plugins.pro +++ y/qtbase/src/plugins/plugins.pro -@@ -8,6 +8,3 @@ qtHaveModule(gui) { - qtConfig(imageformatplugin): SUBDIRS *= imageformats +@@ -9,6 +9,3 @@ qtHaveModule(gui) { !android:qtConfig(library): SUBDIRS *= generic } + qtHaveModule(widgets): SUBDIRS += styles - -!winrt:qtHaveModule(printsupport): \ - SUBDIRS += printsupport diff --git a/depends/patches/qt/fix_powerpc_libpng.patch b/depends/patches/qt/fix_powerpc_libpng.patch deleted file mode 100644 index d37b6c7776..0000000000 --- a/depends/patches/qt/fix_powerpc_libpng.patch +++ /dev/null @@ -1,23 +0,0 @@ -commit 6f9feb773a43c5abfa3455da2e324180e789285b -Author: fanquake -Date: Tue Sep 15 21:44:31 2020 +0800 - - Fix PowerPC build of libpng - - See https://bugreports.qt.io/browse/QTBUG-66388. - - Can be dropped when we are building qt 5.12.0 or later. - -diff --git a/qtbase/src/3rdparty/libpng/libpng.pro b/qtbase/src/3rdparty/libpng/libpng.pro -index 577b61d8..a2f56669 100644 ---- a/qtbase/src/3rdparty/libpng/libpng.pro -+++ b/qtbase/src/3rdparty/libpng/libpng.pro -@@ -10,7 +10,7 @@ MODULE_INCLUDEPATH = $$PWD - - load(qt_helper_lib) - --DEFINES += PNG_ARM_NEON_OPT=0 -+DEFINES += PNG_ARM_NEON_OPT=0 PNG_POWERPC_VSX_OPT=0 - SOURCES += \ - png.c \ - pngerror.c \ diff --git a/depends/patches/qt/fix_qpainter_non_determinism.patch b/depends/patches/qt/fix_qpainter_non_determinism.patch deleted file mode 100644 index 3cfcc22f03..0000000000 --- a/depends/patches/qt/fix_qpainter_non_determinism.patch +++ /dev/null @@ -1,63 +0,0 @@ -commit 2a8f7dc6ddfc414a66491522501c1574a1343ee1 -Author: Andrew Chow -Date: Sat Nov 21 01:11:04 2020 -0500 - - build: Fix determinism issue when building with Clang 8 - - When building Qt with LLVM/Clang 8 under -O3 (the default), we run into - a determinism issue in `qt_interset_spans`. The issue has been fixed for - LLVM/Clang 9, see - https://github.com/llvm/llvm-project/commit/db101864bdc938deb1d63fe4f7da761bd38e5cae - and https://reviews.llvm.org/D64601, however this fix was not backported - to 8.x. Once LLVM/Clang 9 is used, this patch can be dropped. - - The particular issue appears to be an optimization done by -O3 which - adds a temporary variable for `spans->y` in `qt_intersect_spans`. When - it does this, sometimes it chooses to use a 32-bit movs instruction - (movswl), and other times it chooses a 64-bit movs instruction (movswq). - By patching `qt_intersect_spans` to always make a temporary variable for - `spans->y`, we are able to sidestep this problem. - -diff --git a/qtbase/src/gui/painting/qpaintengine_raster.cpp b/qtbase/src/gui/painting/qpaintengine_raster.cpp -index 92ab6e8375..f018009e0b 100644 ---- a/qtbase/src/gui/painting/qpaintengine_raster.cpp -+++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp -@@ -3971,22 +3971,23 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip, - const QSpan *clipEnd = clip->m_spans + clip->count; - - while (available && spans < end ) { -+ const short spans_y = spans->y; - if (clipSpans >= clipEnd) { - spans = end; - break; - } -- if (clipSpans->y > spans->y) { -+ if (clipSpans->y > spans_y) { - ++spans; - continue; - } -- if (spans->y != clipSpans->y) { -- if (spans->y < clip->count && clip->m_clipLines[spans->y].spans) -- clipSpans = clip->m_clipLines[spans->y].spans; -+ if (spans_y != clipSpans->y) { -+ if (spans_y < clip->count && clip->m_clipLines[spans_y].spans) -+ clipSpans = clip->m_clipLines[spans_y].spans; - else - ++clipSpans; - continue; - } -- Q_ASSERT(spans->y == clipSpans->y); -+ Q_ASSERT(spans_y == clipSpans->y); - - int sx1 = spans->x; - int sx2 = sx1 + spans->len; -@@ -4005,7 +4006,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip, - if (len) { - out->x = qMax(sx1, cx1); - out->len = qMin(sx2, cx2) - out->x; -- out->y = spans->y; -+ out->y = spans_y; - out->coverage = qt_div_255(spans->coverage * clipSpans->coverage); - ++out; - --available; - diff --git a/depends/patches/qt/fix_qt_pkgconfig.patch b/depends/patches/qt/fix_qt_pkgconfig.patch index 8c722ffb46..a5de2b4b9e 100644 --- a/depends/patches/qt/fix_qt_pkgconfig.patch +++ b/depends/patches/qt/fix_qt_pkgconfig.patch @@ -1,17 +1,17 @@ --- old/qtbase/mkspecs/features/qt_module.prf +++ new/qtbase/mkspecs/features/qt_module.prf -@@ -264,7 +264,7 @@ +@@ -269,7 +269,7 @@ load(qt_installs) load(qt_targets) # this builds on top of qt_common --!internal_module:!lib_bundle:if(unix|mingw) { +-!internal_module:if(unix|mingw) { +if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) { CONFIG += create_pc QMAKE_PKGCONFIG_DESTDIR = pkgconfig host_build: \ -@@ -274,9 +274,9 @@ - QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_HEADERS/raw] - QMAKE_PKGCONFIG_CFLAGS = -I${includedir}/$$MODULE_INCNAME +@@ -284,9 +284,9 @@ load(qt_targets) + QMAKE_PKGCONFIG_CFLAGS = -D$$MODULE_DEFINE -I${includedir}/$$MODULE_INCNAME + } QMAKE_PKGCONFIG_NAME = $$replace(TARGET, ^Qt, "Qt$$QT_MAJOR_VERSION ") - QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION) + QMAKE_PKGCONFIG_FILE = $$replace(TARGET, ^Qt, Qt$$QT_MAJOR_VERSION)$$qtPlatformTargetSuffix() @@ -20,4 +20,4 @@ + QMAKE_PKGCONFIG_REQUIRES += $$replace(QT.$${i}.name, ^Qt, Qt$$section(QT.$${i}.VERSION, ., 0, 0))$$qtPlatformTargetSuffix() isEmpty(QMAKE_PKGCONFIG_DESCRIPTION): \ QMAKE_PKGCONFIG_DESCRIPTION = $$replace(TARGET, ^Qt, "Qt ") module - pclib_replace.match = $$lib_replace.match + !isEmpty(lib_replace0.match) { diff --git a/depends/patches/qt/fix_rcc_determinism.patch b/depends/patches/qt/fix_rcc_determinism.patch deleted file mode 100644 index c1b07fe23a..0000000000 --- a/depends/patches/qt/fix_rcc_determinism.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- old/qtbase/src/tools/rcc/rcc.cpp -+++ new/qtbase/src/tools/rcc/rcc.cpp -@@ -207,7 +207,11 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) - if (lib.formatVersion() >= 2) { - // last modified time stamp - const QDateTime lastModified = m_fileInfo.lastModified(); -- lib.writeNumber8(quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0)); -+ quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0); -+ static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong(); -+ if (sourceDate != 0) -+ lastmod = sourceDate; -+ lib.writeNumber8(lastmod); - if (text || pass1) - lib.writeChar('\n'); - } diff --git a/depends/patches/qt/fix_riscv64_arch.patch b/depends/patches/qt/fix_riscv64_arch.patch deleted file mode 100644 index e7f29f01f9..0000000000 --- a/depends/patches/qt/fix_riscv64_arch.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h b/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h -index 20bfd36..93729fa 100644 ---- a/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h -+++ b/qtbase/src/3rdparty/double-conversion/include/double-conversion/utils.h -@@ -65,7 +65,8 @@ - defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ - defined(__SH4__) || defined(__alpha__) || \ - defined(_MIPS_ARCH_MIPS32R2) || \ -- defined(__AARCH64EL__) -+ defined(__AARCH64EL__) || defined(__aarch64__) || \ -+ defined(__riscv) - #define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 - #elif defined(_M_IX86) || defined(__i386__) || defined(__i386) - #if defined(_WIN32) diff --git a/depends/patches/qt/freetype_back_compat.patch b/depends/patches/qt/freetype_back_compat.patch deleted file mode 100644 index 1ca55f1ce3..0000000000 --- a/depends/patches/qt/freetype_back_compat.patch +++ /dev/null @@ -1,28 +0,0 @@ -commit 14bc77db61bf9d56f9b6c8b84aa02573605c19c6 -Author: fanquake -Date: Tue Aug 18 15:15:08 2020 +0800 - - Fix backwards compatibility with older Freetype versions at runtime - - A few years ago, libfreetype introduced FT_Get_Font_Format() as an alias - for FT_Get_X11_Font_Format(), but FT_Get_X11_Font_Format() was kept for abi - backwards-compatibility. - - Qt 5.9 introduced a call to FT_Get_Font_Format(). Replace it with FT_Get_X11_Font_Format() - in order to remain compatible with older freetype, which is still used by e.g. Ubuntu Trusty. - - See #14348. - -diff --git a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp -index 3f543755..8ecc1c8c 100644 ---- a/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp -+++ b/qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp -@@ -898,7 +898,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, - } - } - #if defined(FT_FONT_FORMATS_H) -- const char *fmt = FT_Get_Font_Format(face); -+ const char *fmt = FT_Get_X11_Font_Format(face); - if (fmt && qstrncmp(fmt, "CFF", 4) == 0) { - FT_Bool no_stem_darkening = true; - FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening); diff --git a/depends/patches/qt/no-xlib.patch b/depends/patches/qt/no-xlib.patch index fe82c2c73c..f4a6f09ee4 100644 --- a/depends/patches/qt/no-xlib.patch +++ b/depends/patches/qt/no-xlib.patch @@ -22,15 +22,15 @@ index 7c62c2e2b3..c05c6c0a07 100644 #include #include -@@ -384,6 +386,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget) - w->setCursor(c, isBitmapCursor); +@@ -391,6 +393,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window) + xcb_flush(xcb_connection()); } +#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) static int cursorIdForShape(int cshape) { int cursorId = 0; -@@ -437,6 +440,7 @@ static int cursorIdForShape(int cshape) +@@ -444,6 +447,7 @@ static int cursorIdForShape(int cshape) } return cursorId; } @@ -38,7 +38,7 @@ index 7c62c2e2b3..c05c6c0a07 100644 xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) { -@@ -558,7 +562,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) +@@ -556,7 +560,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { xcb_connection_t *conn = xcb_connection(); @@ -48,22 +48,23 @@ index 7c62c2e2b3..c05c6c0a07 100644 xcb_cursor_t cursor = XCB_NONE; // Try Xcursor first -@@ -589,6 +595,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) +@@ -585,7 +591,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) + // Non-standard X11 cursors are created from bitmaps cursor = createNonStandardCursor(cshape); - +- +#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) // Create a glpyh cursor if everything else failed if (!cursor && cursorId) { cursor = xcb_generate_id(conn); -@@ -596,6 +603,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) +@@ -593,6 +599,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) cursorId, cursorId + 1, 0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0); } +#endif if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) { - const char *name = cursorNames[cshape]; + const char *name = cursorNames[cshape].front(); -- 2.22.0 diff --git a/depends/patches/qt/no_sdk_version_check.patch b/depends/patches/qt/no_sdk_version_check.patch new file mode 100644 index 0000000000..b16635b572 --- /dev/null +++ b/depends/patches/qt/no_sdk_version_check.patch @@ -0,0 +1,20 @@ +commit f5eb142cd04be2bc4ca610ed3b5b7e8ce3520ee3 +Author: fanquake +Date: Tue Jan 5 16:08:49 2021 +0800 + + Don't invoke macOS SDK version checking + + This tries to use xcrun which is not available when cross-compiling. + +diff --git a/qtbase/mkspecs/features/mac/default_post.prf b/qtbase/mkspecs/features/mac/default_post.prf +index 92a9112bca6..447e186eb26 100644 +--- a/qtbase/mkspecs/features/mac/default_post.prf ++++ b/qtbase/mkspecs/features/mac/default_post.prf +@@ -8,7 +8,6 @@ contains(TEMPLATE, .*app) { + !macx-xcode:if(isEmpty(BUILDS)|build_pass) { + # Detect changes to the platform SDK + QMAKE_EXTRA_VARIABLES += QMAKE_MAC_SDK QMAKE_MAC_SDK_VERSION QMAKE_XCODE_DEVELOPER_PATH +- QMAKE_EXTRA_INCLUDES += $$shell_quote($$PWD/sdk.mk) + } + + # Detect incompatible SDK versions diff --git a/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch new file mode 100644 index 0000000000..0358bea6e9 --- /dev/null +++ b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch @@ -0,0 +1,17 @@ +The moc executable loops through headers on CPLUS_INCLUDE_PATH and stumbles +on the GCC internal _GLIBCXX_VISIBILITY macro. Tell it to ignore it as it is +not supposed to be looking there to begin with. + +Upstream report: https://bugreports.qt.io/browse/QTBUG-83160 + +diff --git a/qtbase/src/tools/moc/main.cpp b/qtbase/src/tools/moc/main.cpp +--- a/qtbase/src/tools/moc/main.cpp ++++ b/qtbase/src/tools/moc/main.cpp +@@ -188,6 +188,7 @@ int runMoc(int argc, char **argv) + dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER, "__VA_ARGS__"); + pp.macros["__attribute__"] = dummyVariadicFunctionMacro; + pp.macros["__declspec"] = dummyVariadicFunctionMacro; ++ pp.macros["_GLIBCXX_VISIBILITY"] = dummyVariadicFunctionMacro; + + QString filename; + QString output; diff --git a/depends/patches/qt/xkb-default.patch b/depends/patches/qt/xkb-default.patch deleted file mode 100644 index 165abf3e2e..0000000000 --- a/depends/patches/qt/xkb-default.patch +++ /dev/null @@ -1,26 +0,0 @@ ---- old/qtbase/src/gui/configure.pri 2018-06-06 17:28:10.000000000 -0400 -+++ new/qtbase/src/gui/configure.pri 2018-08-17 18:43:01.589384567 -0400 -@@ -43,18 +43,11 @@ - } - - defineTest(qtConfTest_xkbConfigRoot) { -- qtConfTest_getPkgConfigVariable($${1}): return(true) -- -- for (dir, $$list("/usr/share/X11/xkb", "/usr/local/share/X11/xkb")) { -- exists($$dir) { -- $${1}.value = $$dir -- export($${1}.value) -- $${1}.cache += value -- export($${1}.cache) -- return(true) -- } -- } -- return(false) -+ $${1}.value = "/usr/share/X11/xkb" -+ export($${1}.value) -+ $${1}.cache += value -+ export($${1}.cache) -+ return(true) - } - - defineTest(qtConfTest_qpaDefaultPlatform) { From eec6abf64d4c4579cc0681599ca36e8c3826366e Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Fri, 14 May 2021 00:31:19 -0500 Subject: [PATCH 094/280] Update Qt autoconf macros from Bitcoin bitcoin/bitcoin@2045e4cdd26920665300f21be5b08fe66bde4a41 This update supports cross-compiling for Windows with Qt 5.12.10 from the updated depends recipe. --- build-aux/m4/bitcoin_qt.m4 | 186 ++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 73 deletions(-) diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index 6fdd0bfe25..d9a73d7865 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -108,13 +108,10 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS]) dnl This is ugly and complicated. Yuck. Works as follows: - dnl For Qt5, we can check a header to find out whether Qt is build - dnl statically. When Qt is built statically, some plugins must be linked into - dnl the final binary as well. - dnl With Qt5, languages moved into core and the WindowsIntegration plugin was - dnl added. - dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the - dnl results to QT_LIBS. + dnl We check a header to find out whether Qt is built statically. + dnl When Qt is built statically, some plugins must be linked into + dnl the final binary as well. _BITCOIN_QT_CHECK_STATIC_PLUGIN does + dnl a quick link-check and appends the results to QT_LIBS. BITCOIN_QT_CHECK([ TEMP_CPPFLAGS=$CPPFLAGS TEMP_CXXFLAGS=$CXXFLAGS @@ -122,35 +119,68 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ CXXFLAGS="$PIC_FLAGS $CXXFLAGS" _BITCOIN_QT_IS_STATIC if test "x$bitcoin_cv_static_qt" = xyes; then - _BITCOIN_QT_FIND_STATIC_PLUGINS + _BITCOIN_QT_CHECK_STATIC_LIBS + + if test "x$qt_plugin_path" != x; then + if test -d "$qt_plugin_path/platforms"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" + fi + if test -d "$qt_plugin_path/styles"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/styles" + fi + if test -d "$qt_plugin_path/accessible"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" + fi + if test -d "$qt_plugin_path/platforms/android"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL" + fi + dnl Gridcoin uses SVG: + if test -d "$qt_plugin_path/imageformats"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/imageformats" + fi + if test -d "$qt_plugin_path/iconengines"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/iconengines" + fi + fi + AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) if test "x$TARGET_OS" != xandroid; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QMinimalIntegrationPlugin], [-lqminimal]) AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists]) fi if test "x$TARGET_OS" = xwindows; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) + dnl Linking against wtsapi32 is required. See bitcoin/bitcoin#17749 and + dnl https://bugreports.qt.io/browse/QTBUG-27097. + AX_CHECK_LINK_FLAG([-lwtsapi32], [QT_LIBS="$QT_LIBS -lwtsapi32"], [AC_MSG_ERROR([could not link against -lwtsapi32])]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QWindowsIntegrationPlugin], [-lqwindows]) AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows]) elif test "x$TARGET_OS" = xlinux; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static]) + dnl workaround for https://bugreports.qt.io/browse/QTBUG-74874 + AX_CHECK_LINK_FLAG([-lxcb-shm], [QT_LIBS="-lxcb-shm $QT_LIBS"], [AC_MSG_ERROR([could not link against -lxcb-shm])]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QXcbIntegrationPlugin], [-lqxcb]) AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) elif test "x$TARGET_OS" = xdarwin; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa]) + AX_CHECK_LINK_FLAG([[-framework Carbon]],[QT_LIBS="$QT_LIBS -framework Carbon"],[AC_MSG_ERROR(could not link against Carbon framework)]) + AX_CHECK_LINK_FLAG([[-framework IOSurface]],[QT_LIBS="$QT_LIBS -framework IOSurface"],[AC_MSG_ERROR(could not link against IOSurface framework)]) + AX_CHECK_LINK_FLAG([[-framework Metal]],[QT_LIBS="$QT_LIBS -framework Metal"],[AC_MSG_ERROR(could not link against Metal framework)]) + AX_CHECK_LINK_FLAG([[-framework QuartzCore]],[QT_LIBS="$QT_LIBS -framework QuartzCore"],[AC_MSG_ERROR(could not link against QuartzCore framework)]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QCocoaIntegrationPlugin], [-lqcocoa]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QMacStylePlugin], [-lqmacstyle]) AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa]) elif test "x$TARGET_OS" = xandroid; then - QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype -lQt5EglSupport $QT_LIBS" + QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype $QT_LIBS" AC_DEFINE(QT_QPA_PLATFORM_ANDROID, 1, [Define this symbol if the qt platform is android]) fi dnl Gridcoin uses SVG: - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QSvgPlugin)],[-lqsvg]) - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QSvgIconPlugin)],[-lqsvgicon]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QSvgPlugin], [-lqsvg]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QSvgIconPlugin], [-lqsvgicon]) fi CPPFLAGS=$TEMP_CPPFLAGS CXXFLAGS=$TEMP_CXXFLAGS ]) if test "x$qt_bin_path" = x; then - qt_bin_path="`$PKG_CONFIG --variable=host_bins Qt5Core 2>/dev/null`" + qt_bin_path="`$PKG_CONFIG --variable=host_bins ${qt_lib_prefix}Core 2>/dev/null`" fi if test "x$use_hardening" != xno; then @@ -205,6 +235,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ BITCOIN_QT_PATH_PROGS([RCC], [rcc-qt5 rcc5 rcc], $qt_bin_path) BITCOIN_QT_PATH_PROGS([LRELEASE], [lrelease-qt5 lrelease5 lrelease], $qt_bin_path) BITCOIN_QT_PATH_PROGS([LUPDATE], [lupdate-qt5 lupdate5 lupdate],$qt_bin_path, yes) + BITCOIN_QT_PATH_PROGS([LCONVERT], [lconvert-qt5 lconvert5 lconvert], $qt_bin_path, yes) MOC_DEFS='-DHAVE_CONFIG_H -I$(srcdir)' case $host in @@ -238,7 +269,10 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ AC_MSG_ERROR([libQtDBus not found. Install libQtDBus or remove --with-qtdbus.]) fi if test "x$LUPDATE" = x; then - AC_MSG_WARN([lupdate is required to update qt translations]) + AC_MSG_WARN([lupdate tool is required to update Qt translations.]) + fi + if test "x$LCONVERT" = x; then + AC_MSG_WARN([lconvert tool is required to update Qt translations.]) fi ],[ bitcoin_enable_qt=no @@ -273,12 +307,13 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ fi ]) -dnl All macros below are internal and should _not_ be used from the main -dnl configure.ac. -dnl ---- +dnl All macros below are internal and should _not_ be used from configure.ac. -dnl Internal. Check if the linked version of Qt was built as static libs. -dnl Requires: Qt5. +dnl Internal. Check if the linked version of Qt was built statically. +dnl +dnl _BITCOIN_QT_IS_STATIC +dnl --------------------- +dnl dnl Requires: INCLUDES and LIBS must be populated as necessary. dnl Output: bitcoin_cv_static_qt=yes|no AC_DEFUN([_BITCOIN_QT_IS_STATIC],[ @@ -299,93 +334,98 @@ AC_DEFUN([_BITCOIN_QT_IS_STATIC],[ ]) ]) -dnl Internal. Check if the link-requirements for static plugins are met. +dnl Internal. Check if the link-requirements for a static plugin are met. +dnl +dnl _BITCOIN_QT_CHECK_STATIC_PLUGIN(PLUGIN, LIBRARIES) +dnl -------------------------------------------------- +dnl dnl Requires: INCLUDES and LIBS must be populated as necessary. -dnl Inputs: $1: A series of Q_IMPORT_PLUGIN(). +dnl Inputs: $1: A static plugin name. dnl Inputs: $2: The libraries that resolve $1. dnl Output: QT_LIBS is prepended or configure exits. -AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGINS],[ - AC_MSG_CHECKING(for static Qt plugins: $2) +AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGIN], [ + AC_MSG_CHECKING([for $1 ($2)]) CHECK_STATIC_PLUGINS_TEMP_LIBS="$LIBS" LIBS="$2${qt_lib_suffix} $QT_LIBS $LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #define QT_STATICPLUGIN - #include - $1]], - [[return 0;]])], - [AC_MSG_RESULT(yes); QT_LIBS="$2${qt_lib_suffix} $QT_LIBS"], - [AC_MSG_RESULT(no); BITCOIN_QT_FAIL(Could not resolve: $2)]) + #include + Q_IMPORT_PLUGIN($1) + ]])], + [AC_MSG_RESULT([yes]); QT_LIBS="$2${qt_lib_suffix} $QT_LIBS"], + [AC_MSG_RESULT([no]); BITCOIN_QT_FAIL([$1 not found.])]) LIBS="$CHECK_STATIC_PLUGINS_TEMP_LIBS" ]) -dnl Internal. Find paths necessary for linking qt static plugins -dnl Inputs: qt_plugin_path. optional. -dnl Outputs: QT_LIBS is appended -AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ - if test "x$qt_plugin_path" != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms -L$qt_plugin_path/imageformats -L$qt_plugin_path/iconengines" - if test -d "$qt_plugin_path/accessible"; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - fi - if test -d "$qt_plugin_path/platforms/android"; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL" - fi - PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FontDatabaseSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="-lQt5EventDispatcherSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ThemeSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="-lQt5DeviceDiscoverySupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="-lQt5AccessibilitySupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTFB], [Qt5FbSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FbSupport${qt_lib_suffix} $QT_LIBS"]) - dnl Gridcoin uses Concurrent: - PKG_CHECK_MODULES([QTCONCURRENT], [Qt5Concurrent${qt_lib_suffix}], [QT_LIBS="-lQt5Concurrent${qt_lib_suffix} $QT_LIBS"]) - dnl Gridcoin uses Charts: - PKG_CHECK_MODULES([QTCHARTS], [Qt5Charts${qt_lib_suffix}], [QT_LIBS="-lQt5Charts${qt_lib_suffix} $QT_LIBS"]) - dnl Gridcoin uses SVG: - PKG_CHECK_MODULES([QTSVG], [Qt5Svg${qt_lib_suffix}], [QT_LIBS="-lQt5Svg${qt_lib_suffix} $QT_LIBS"]) - if test "x$TARGET_OS" = xlinux; then - PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) - elif test "x$TARGET_OS" = xdarwin; then - PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ClipboardSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport${qt_lib_suffix}], [QT_LIBS="-lQt5GraphicsSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport${qt_lib_suffix}], [QT_LIBS="-lQt5CglSupport${qt_lib_suffix} $QT_LIBS"]) - fi - fi +dnl Internal. Check Qt static libs with PKG_CHECK_MODULES. +dnl +dnl _BITCOIN_QT_CHECK_STATIC_LIBS +dnl ----------------------------- +dnl +dnl Outputs: QT_LIBS is prepended. +AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [ + PKG_CHECK_MODULES([QT_ACCESSIBILITY], [${qt_lib_prefix}AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="$QT_ACCESSIBILITY_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_DEVICEDISCOVERY], [${qt_lib_prefix}DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="$QT_DEVICEDISCOVERY_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_EDID], [${qt_lib_prefix}EdidSupport${qt_lib_suffix}], [QT_LIBS="$QT_EDID_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_EVENTDISPATCHER], [${qt_lib_prefix}EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="$QT_EVENTDISPATCHER_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_FB], [${qt_lib_prefix}FbSupport${qt_lib_suffix}], [QT_LIBS="$QT_FB_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_FONTDATABASE], [${qt_lib_prefix}FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="$QT_FONTDATABASE_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_THEME], [${qt_lib_prefix}ThemeSupport${qt_lib_suffix}], [QT_LIBS="$QT_THEME_LIBS $QT_LIBS"]) + dnl Gridcoin uses Concurrent: + PKG_CHECK_MODULES([QT_CONCURRENT], [${qt_lib_prefix}Concurrent${qt_lib_suffix}], [QT_LIBS="$QT_CONCURRENT_LIBS $QT_LIBS"]) + dnl Gridcoin uses Charts: + PKG_CHECK_MODULES([QT_CHARTS], [${qt_lib_prefix}Charts${qt_lib_suffix}], [QT_LIBS="$QT_CONCURRENT_LIBS $QT_LIBS"]) + dnl Gridcoin uses SVG: + PKG_CHECK_MODULES([QT_SVG], [${qt_lib_prefix}Svg${qt_lib_suffix}], [QT_LIBS="$QT_SVG_LIBS $QT_LIBS"]) + if test "x$TARGET_OS" = xlinux; then + PKG_CHECK_MODULES([QT_INPUT], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_INPUT_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_XCBQPA], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_XCBQPA_LIBS $QT_LIBS"]) + elif test "x$TARGET_OS" = xdarwin; then + PKG_CHECK_MODULES([QT_CLIPBOARD], [${qt_lib_prefix}ClipboardSupport${qt_lib_suffix}], [QT_LIBS="$QT_CLIPBOARD_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_GRAPHICS], [${qt_lib_prefix}GraphicsSupport${qt_lib_suffix}], [QT_LIBS="$QT_GRAPHICS_LIBS $QT_LIBS"]) + PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport${qt_lib_suffix}], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"]) + elif test "x$TARGET_OS" = xwindows; then + PKG_CHECK_MODULES([QT_WINDOWSUIAUTOMATION], [${qt_lib_prefix}WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="$QT_WINDOWSUIAUTOMATION_LIBS $QT_LIBS"]) + elif test "x$TARGET_OS" = xandroid; then + PKG_CHECK_MODULES([QT_EGL], [${qt_lib_prefix}EglSupport], [QT_LIBS="$QT_EGL_LIBS $QT_LIBS"]) + fi ]) dnl Internal. Find Qt libraries using pkg-config. +dnl +dnl _BITCOIN_QT_FIND_LIBS +dnl --------------------- +dnl dnl Outputs: All necessary QT_* variables are set. dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no. AC_DEFUN([_BITCOIN_QT_FIND_LIBS],[ BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core${qt_lib_suffix} $qt_version], [], + PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CORE_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CORE_LIBS $QT_LIBS"], [BITCOIN_QT_FAIL([${qt_lib_prefix}Core${qt_lib_suffix} $qt_version not found])]) ]) BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version], [], + PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_GUI_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_GUI_LIBS $QT_LIBS"], [BITCOIN_QT_FAIL([${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version not found])]) ]) BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version], [], + PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_WIDGETS_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_WIDGETS_LIBS $QT_LIBS"], [BITCOIN_QT_FAIL([${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version not found])]) ]) BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network${qt_lib_suffix} $qt_version], [], + PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_NETWORK_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_NETWORK_LIBS $QT_LIBS"], [BITCOIN_QT_FAIL([${qt_lib_prefix}Network${qt_lib_suffix} $qt_version not found])]) ]) dnl Gridcoin uses Concurrent: BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_CONCURRENT], [${qt_lib_prefix}Concurrent${qt_lib_suffix} $qt_version], [], + PKG_CHECK_MODULES([QT_CONCURRENT], [${qt_lib_prefix}Concurrent${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CONCURRENT_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CONCURRENT_LIBS $QT_LIBS"], [BITCOIN_QT_FAIL([${qt_lib_prefix}Concurrent${qt_lib_suffix} $qt_version not found])]) ]) dnl Gridcoin uses Charts: BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_CHARTS], [${qt_lib_prefix}Charts${qt_lib_suffix} $qt_version], [CPPFLAGS="$CPPFLAGS -DQT_CHARTS_LIB"], + PKG_CHECK_MODULES([QT_CHARTS], [${qt_lib_prefix}Charts${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CHARTS_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CHARTS_LIBS $QT_LIBS" CPPFLAGS="$CPPFLAGS -DQT_CHARTS_LIB"], [AC_MSG_WARN([${qt_lib_prefix}Charts${qt_lib_suffix} $qt_version not found. Poll results will not display charts.])]) ]) - QT_INCLUDES="$QT_CORE_CFLAGS $QT_GUI_CFLAGS $QT_WIDGETS_CFLAGS $QT_NETWORK_CFLAGS $QT_CONCURRENT_CFLAGS $QT_CHARTS_CFLAGS" - QT_LIBS="$QT_CORE_LIBS $QT_GUI_LIBS $QT_WIDGETS_LIBS $QT_NETWORK_LIBS $QT_CONCURRENT_LIBS $QT_CHARTS_LIBS" - BITCOIN_QT_CHECK([ PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test${qt_lib_suffix} $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no]) if test "x$use_dbus" != xno; then From 49d7495801a6a252f59595d64c235b45dfcf489e Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Fri, 14 May 2021 00:33:41 -0500 Subject: [PATCH 095/280] Remove unused depends Linux GUI recipes These recipes were unused since upgrading to Qt 5.9: - dbus - libX11 - libXext - xextproto - xtrans --- depends/packages/dbus.mk | 28 ---------------------------- depends/packages/libX11.mk | 28 ---------------------------- depends/packages/libXext.mk | 27 --------------------------- depends/packages/packages.mk | 2 +- depends/packages/xextproto.mk | 30 ------------------------------ depends/packages/xtrans.mk | 27 --------------------------- 6 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 depends/packages/dbus.mk delete mode 100644 depends/packages/libX11.mk delete mode 100644 depends/packages/libXext.mk delete mode 100644 depends/packages/xextproto.mk delete mode 100644 depends/packages/xtrans.mk diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk deleted file mode 100644 index fe8ddae1b7..0000000000 --- a/depends/packages/dbus.mk +++ /dev/null @@ -1,28 +0,0 @@ -package=dbus -GCCFLAGS?= -$(package)_version=1.10.18 -$(package)_download_path=https://dbus.freedesktop.org/releases/dbus -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a -$(package)_dependencies=expat - -define $(package)_set_vars - $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-static --without-x - $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) - $(package)_cflags_aarch64_linux = $(GCCFLAGS) - $(package)_cxxflags_arm_linux = $(GCCFLAGS) - $(package)_cflags_arm_linux = $(GCCFLAGS) -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -C dbus libdbus-1.la -endef - -define $(package)_stage_cmds - $(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \ - $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA -endef diff --git a/depends/packages/libX11.mk b/depends/packages/libX11.mk deleted file mode 100644 index 40b7452bdd..0000000000 --- a/depends/packages/libX11.mk +++ /dev/null @@ -1,28 +0,0 @@ -package=libX11 -GCCFLAGS?= -$(package)_version=1.6.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2aa027e837231d2eeea90f3a4afe19948a6eb4c8b2bec0241eba7dbc8106bd16 -$(package)_dependencies=libxcb xtrans xextproto xproto - -define $(package)_set_vars - $(package)_config_opts=--disable-xkb --disable-static - $(package)_config_opts_linux=--with-pic - $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) - $(package)_cflags_aarch64_linux = $(GCCFLAGS) - $(package)_cxxflags_arm_linux = $(GCCFLAGS) - $(package)_cflags_arm_linux = $(GCCFLAGS) -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/depends/packages/libXext.mk b/depends/packages/libXext.mk deleted file mode 100644 index dc7bf67c40..0000000000 --- a/depends/packages/libXext.mk +++ /dev/null @@ -1,27 +0,0 @@ -package=libXext -GCCFLAGS?= -$(package)_version=1.3.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=f829075bc646cdc085fa25d98d5885d83b1759ceb355933127c257e8e50432e0 -$(package)_dependencies=xproto xextproto libX11 libXau - -define $(package)_set_vars - $(package)_config_opts=--disable-static - $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) - $(package)_cflags_aarch64_linux = $(GCCFLAGS) - $(package)_cxxflags_arm_linux = $(GCCFLAGS) - $(package)_cflags_arm_linux = $(GCCFLAGS) -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 45f23dccbe..56ee5b43bd 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -3,7 +3,7 @@ native_packages := native_ccache qt_packages = qrencode -qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans libxkbcommon +qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon qt_darwin_packages=qt qt_mingw32_packages=qt diff --git a/depends/packages/xextproto.mk b/depends/packages/xextproto.mk deleted file mode 100644 index 2244d790f2..0000000000 --- a/depends/packages/xextproto.mk +++ /dev/null @@ -1,30 +0,0 @@ -package=xextproto -GCCFLAGS?= -$(package)_version=7.3.0 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=f3f4b23ac8db9c3a9e0d8edb591713f3d70ef9c3b175970dd8823dfc92aa5bb0 - -define $(package)_preprocess_cmds - cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub . -endef - -define $(package)_set_vars - $(package)_config_opts=--disable-shared - $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) - $(package)_cflags_aarch64_linux = $(GCCFLAGS) - $(package)_cxxflags_arm_linux = $(GCCFLAGS) - $(package)_cflags_arm_linux = $(GCCFLAGS) -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/depends/packages/xtrans.mk b/depends/packages/xtrans.mk deleted file mode 100644 index fc7c98797f..0000000000 --- a/depends/packages/xtrans.mk +++ /dev/null @@ -1,27 +0,0 @@ -package=xtrans -GCCFLAGS?= -$(package)_version=1.3.4 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=054d4ee3efd52508c753e9f7bc655ef185a29bd2850dd9e2fc2ccc33544f583a -$(package)_dependencies= - -define $(package)_set_vars - $(package)_config_opts_linux=--with-pic --disable-static - $(package)_cxxflags_aarch64_linux = $(GCCFLAGS) - $(package)_cflags_aarch64_linux = $(GCCFLAGS) - $(package)_cxxflags_arm_linux = $(GCCFLAGS) - $(package)_cflags_arm_linux = $(GCCFLAGS) -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef From fd6b13144731813d7c461a33fc8500cae1c98017 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Wed, 5 May 2021 14:23:33 -0400 Subject: [PATCH 096/280] Implement consolidateunspent wizard This implements a three step wizard that leads the user through the process of selecting inputs, selecting a destination, and then reviewing the overall transaction. Select Inputs: The select inputs screen uses similar code to that in coincontroldialog to support the consolidate button there. Pointers to the coincontrol data structures constructed in sendcoinsdialog are passed into both coincontrol and the consolidateunspentwizard to faciliate using the underlying machinery in a unified manner. This is possible because both coincontrol and consolidateunspendwizard are called with the sendcoinsdialog and are modal. Note that there is a stop sign and the next button is disabled if more than 600 outputs are selected. The next button is also disabled if less than 2 outputs are selected (as it makes no sense to consolidate when there is no consolidation achievable based on the selection). If a filter operation is applied based on the filter criteria, and that criteria would result in more than 600 inputs being selected, the selection is reduced to 600 inputs and a warning triangle is presented. Select Destination Address: If all of the inputs selected in the prior page are from one address, then that address will already be preselected (but allow the opportunity for it to be changed by the user prior to pressing next). If the inputs selected correspond to more than one address, an address will NOT be pre-selected, and the user will be required to pick an address to proceed. The next button will be disabled until a valid address is selected for the destination. Confirmation: The final screen presents the details of the intended transaction for review by the user. When the "Finish" button is pressed, the transaction to send is filled in in the send dialog sreen, and the user can review the details again if desired and press the send button to send. --- src/Makefile.qt.include | 16 + src/qt/coincontroldialog.cpp | 27 +- src/qt/coincontroldialog.h | 11 +- src/qt/consolidateunspentdialog.h | 2 +- src/qt/consolidateunspentwizard.cpp | 50 ++ src/qt/consolidateunspentwizard.h | 51 ++ ...dateunspentwizardselectdestinationpage.cpp | 132 ++++ ...lidateunspentwizardselectdestinationpage.h | 36 + ...nsolidateunspentwizardselectinputspage.cpp | 719 ++++++++++++++++++ ...consolidateunspentwizardselectinputspage.h | 91 +++ src/qt/consolidateunspentwizardsendpage.cpp | 53 ++ src/qt/consolidateunspentwizardsendpage.h | 39 + src/qt/forms/consolidateunspentwizard.ui | 69 ++ ...idateunspentwizardselectdestinationpage.ui | 132 ++++ ...onsolidateunspentwizardselectinputspage.ui | 371 +++++++++ .../forms/consolidateunspentwizardsendpage.ui | 119 +++ src/qt/forms/sendcoinsdialog.ui | 9 +- src/qt/sendcoinsdialog.cpp | 55 +- src/qt/sendcoinsdialog.h | 3 + 19 files changed, 1954 insertions(+), 31 deletions(-) create mode 100644 src/qt/consolidateunspentwizard.cpp create mode 100644 src/qt/consolidateunspentwizard.h create mode 100644 src/qt/consolidateunspentwizardselectdestinationpage.cpp create mode 100644 src/qt/consolidateunspentwizardselectdestinationpage.h create mode 100644 src/qt/consolidateunspentwizardselectinputspage.cpp create mode 100644 src/qt/consolidateunspentwizardselectinputspage.h create mode 100644 src/qt/consolidateunspentwizardsendpage.cpp create mode 100644 src/qt/consolidateunspentwizardsendpage.h create mode 100644 src/qt/forms/consolidateunspentwizard.ui create mode 100644 src/qt/forms/consolidateunspentwizardselectdestinationpage.ui create mode 100644 src/qt/forms/consolidateunspentwizardselectinputspage.ui create mode 100644 src/qt/forms/consolidateunspentwizardsendpage.ui diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 2c7aa67182..091f8ca1d4 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -75,6 +75,10 @@ QT_FORMS_UI = \ qt/forms/aboutdialog.ui \ qt/forms/coincontroldialog.ui \ qt/forms/consolidateunspentdialog.ui \ + qt/forms/consolidateunspentwizard.ui \ + qt/forms/consolidateunspentwizardselectdestinationpage.ui \ + qt/forms/consolidateunspentwizardselectinputspage.ui \ + qt/forms/consolidateunspentwizardsendpage.ui \ qt/forms/diagnosticsdialog.ui \ qt/forms/optionsdialog.ui \ qt/forms/rpcconsole.ui \ @@ -113,6 +117,10 @@ QT_MOC_CPP = \ qt/moc_coincontroldialog.cpp \ qt/moc_coincontroltreewidget.cpp \ qt/moc_consolidateunspentdialog.cpp \ + qt/moc_consolidateunspentwizard.cpp \ + qt/moc_consolidateunspentwizardselectdestinationpage.cpp \ + qt/moc_consolidateunspentwizardselectinputspage.cpp \ + qt/moc_consolidateunspentwizardsendpage.cpp \ qt/moc_csvmodelwriter.cpp \ qt/moc_diagnosticsdialog.cpp \ qt/moc_editaddressdialog.cpp \ @@ -184,6 +192,10 @@ GRIDCOINRESEARCH_QT_H = \ qt/coincontroldialog.h \ qt/coincontroltreewidget.h \ qt/consolidateunspentdialog.h \ + qt/consolidateunspentwizard.h \ + qt/consolidateunspentwizardselectdestinationpage.h \ + qt/consolidateunspentwizardselectinputspage.h \ + qt/consolidateunspentwizardsendpage.h \ qt/csvmodelwriter.h \ qt/decoration.h \ qt/diagnosticsdialog.h \ @@ -247,6 +259,10 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/coincontroldialog.cpp \ qt/coincontroltreewidget.cpp \ qt/consolidateunspentdialog.cpp \ + qt/consolidateunspentwizard.cpp \ + qt/consolidateunspentwizardselectdestinationpage.cpp \ + qt/consolidateunspentwizardselectinputspage.cpp \ + qt/consolidateunspentwizardsendpage.cpp \ qt/csvmodelwriter.cpp \ qt/decoration.cpp \ qt/diagnosticsdialog.cpp \ diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index dd605f2104..6c97b5e5e9 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -25,15 +25,17 @@ #include using namespace std; -QList CoinControlDialog::payAmounts; -CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); -CoinControlDialog::CoinControlDialog(QWidget *parent) : +CoinControlDialog::CoinControlDialog(QWidget *parent, CCoinControl *coinControl, QList *payAmounts) : QDialog(parent), m_inputSelectionLimit(GetMaxInputsForConsolidationTxn()), ui(new Ui::CoinControlDialog), + coinControl(coinControl), + payAmounts(payAmounts), model(0) { + assert(coinControl != nullptr && payAmounts != nullptr); + ui->setupUi(this); // context menu actions @@ -158,7 +160,7 @@ void CoinControlDialog::setModel(WalletModel *model) { updateView(); //updateLabelLocked(); - CoinControlDialog::updateLabels(model, this); + CoinControlDialog::updateLabels(model, coinControl, payAmounts, this); } } @@ -226,7 +228,7 @@ void CoinControlDialog::buttonSelectAllClicked() ui->selectAllPushButton->setText("Select None"); } - CoinControlDialog::updateLabels(model, this); + CoinControlDialog::updateLabels(model, coinControl, payAmounts, this); showHideConsolidationReadyToSend(); } @@ -353,7 +355,7 @@ bool CoinControlDialog::filterInputsByValue(const bool& less, const CAmount& inp // Reenable update signals. ui->treeWidget->setEnabled(true); - CoinControlDialog::updateLabels(model, this); + CoinControlDialog::updateLabels(model, coinControl, payAmounts, this); // If the number of inputs selected was limited, then true is returned. return culled_inputs; @@ -596,7 +598,9 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) // selection changed -> update labels if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all - CoinControlDialog::updateLabels(model, this); + { + CoinControlDialog::updateLabels(model, coinControl, payAmounts, this); + } } showHideConsolidationReadyToSend(); @@ -633,7 +637,10 @@ QString CoinControlDialog::getPriorityLabel(double dPriority) else ui->labelLocked->setVisible(false); }*/ -void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) +void CoinControlDialog::updateLabels(WalletModel *model, + CCoinControl *coinControl, + QList* payAmounts, + QDialog* dialog) { if (!model) return; @@ -642,7 +649,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) bool fLowOutput = false; bool fDust = false; CTransaction txDummy; - foreach(const qint64 &amount, CoinControlDialog::payAmounts) + foreach(const qint64 &amount, *payAmounts) { nPayAmount += amount; @@ -704,7 +711,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nQuantity > 0) { // Bytes - nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here + nBytes = nBytesInputs + ((payAmounts->size() > 0 ? payAmounts->size() + 1 : 2) * 34) + 10; // always assume +1 output for change here // Priority dPriority = dPriorityInputs / nBytes; diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index b948181bef..820261b822 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -24,18 +24,17 @@ class CoinControlDialog : public QDialog Q_OBJECT public: - explicit CoinControlDialog(QWidget *parent = 0); + explicit CoinControlDialog(QWidget *parent = 0, + CCoinControl *coinControl = nullptr, + QList *payAmounts = nullptr); ~CoinControlDialog(); void setModel(WalletModel *model); // static because also called from sendcoinsdialog - static void updateLabels(WalletModel*, QDialog*); + static void updateLabels(WalletModel*, CCoinControl*, QList*, QDialog*); static QString getPriorityLabel(double); - static QList payAmounts; - static CCoinControl *coinControl; - // This is based on what will guarantee a successful transaction. const size_t m_inputSelectionLimit; @@ -47,6 +46,8 @@ public slots: private: Ui::CoinControlDialog *ui; + CCoinControl *coinControl; + QList *payAmounts; WalletModel *model; int sortColumn; Qt::SortOrder sortOrder; diff --git a/src/qt/consolidateunspentdialog.h b/src/qt/consolidateunspentdialog.h index 440e4f160b..da9687bdf6 100644 --- a/src/qt/consolidateunspentdialog.h +++ b/src/qt/consolidateunspentdialog.h @@ -14,7 +14,7 @@ class ConsolidateUnspentDialog : public QDialog Q_OBJECT public: - explicit ConsolidateUnspentDialog(QWidget *parent = 0, size_t inputSelectionLimit = 600); + explicit ConsolidateUnspentDialog(QWidget *parent = nullptr, size_t inputSelectionLimit = 600); ~ConsolidateUnspentDialog(); void SetAddressList(const std::map& addressList); diff --git a/src/qt/consolidateunspentwizard.cpp b/src/qt/consolidateunspentwizard.cpp new file mode 100644 index 0000000000..b32f4f8a7c --- /dev/null +++ b/src/qt/consolidateunspentwizard.cpp @@ -0,0 +1,50 @@ +#include "coincontroldialog.h" +#include "consolidateunspentwizard.h" +#include "consolidateunspentdialog.h" +#include "ui_consolidateunspentwizard.h" + +#include "util.h" + + +ConsolidateUnspentWizard::ConsolidateUnspentWizard(QWidget *parent, + CCoinControl *coinControl, + QList *payAmounts) : + QWizard(parent), + ui(new Ui::ConsolidateUnspentWizard), + coinControl(coinControl), + payAmounts(payAmounts) +{ + ui->setupUi(this); + this->setStartId(SelectInputsPage); + + ui->selectInputsPage->setCoinControl(coinControl); + ui->selectInputsPage->setPayAmounts(payAmounts); + + connect(ui->selectInputsPage, SIGNAL(setAddressListSignal(std::map)), + ui->selectDestinationPage, SLOT(SetAddressList(const std::map))); + + connect(ui->selectInputsPage, SIGNAL(setDefaultAddressSignal(QString)), + ui->selectDestinationPage, SLOT(setDefaultAddressSelection(QString))); + + connect(this->button(QWizard::FinishButton), SIGNAL(clicked()), ui->sendPage, SLOT(onFinishButtonClicked())); + connect(ui->sendPage, SIGNAL(selectedConsolidationRecipientSignal(SendCoinsRecipient)), + this, SIGNAL(selectedConsolidationRecipientSignal(SendCoinsRecipient))); +} + +ConsolidateUnspentWizard::~ConsolidateUnspentWizard() +{ + delete ui; +} + +void ConsolidateUnspentWizard::accept() +{ + QDialog::accept(); +} + +void ConsolidateUnspentWizard::setModel(WalletModel *model) +{ + this->model = model; + + ui->selectInputsPage->setModel(model); + ui->sendPage->setModel(model); +} diff --git a/src/qt/consolidateunspentwizard.h b/src/qt/consolidateunspentwizard.h new file mode 100644 index 0000000000..ae1d38d45c --- /dev/null +++ b/src/qt/consolidateunspentwizard.h @@ -0,0 +1,51 @@ +#ifndef CONSOLIDATEUNSPENTWIZARD_H +#define CONSOLIDATEUNSPENTWIZARD_H + +#include "walletmodel.h" + +#include +#include +#include +#include +#include + +namespace Ui { + class ConsolidateUnspentWizard; +} + +class CoinControlDialog; + +class ConsolidateUnspentWizard : public QWizard +{ + Q_OBJECT + +public: + enum Pages + { + SelectInputsPage, + SelectDestinationPage, + SendPage + }; + + explicit ConsolidateUnspentWizard(QWidget *parent = nullptr, + CCoinControl *coinControl = nullptr, + QList *payAmounts = nullptr); + ~ConsolidateUnspentWizard(); + + void setModel(WalletModel *model); + + void accept() override; + +signals: + void passCoinControlSignal(CCoinControl*); + void selectedConsolidationRecipientSignal(SendCoinsRecipient); + void sendConsolidationTransactionSignal(); + +private: + Ui::ConsolidateUnspentWizard *ui; + CCoinControl *coinControl; + QList *payAmounts; + WalletModel *model; +}; + +#endif // CONSOLIDATEUNSPENTWIZARD_H diff --git a/src/qt/consolidateunspentwizardselectdestinationpage.cpp b/src/qt/consolidateunspentwizardselectdestinationpage.cpp new file mode 100644 index 0000000000..238feae3a1 --- /dev/null +++ b/src/qt/consolidateunspentwizardselectdestinationpage.cpp @@ -0,0 +1,132 @@ +#include "consolidateunspentwizardselectdestinationpage.h" +#include "ui_consolidateunspentwizardselectdestinationpage.h" + +#include "util.h" + +ConsolidateUnspentWizardSelectDestinationPage::ConsolidateUnspentWizardSelectDestinationPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::ConsolidateUnspentWizardSelectDestinationPage) +{ + ui->setupUi(this); + + ui->addressTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + + // destination address selection + connect(ui->addressTableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(addressSelectionChanged())); + + ui->isCompleteCheckBox->hide(); + + // This is to provide a convenient way to populate the fields shown on the last page ("send" screen). + registerField("selectedAddressLabelField", ui->selectedAddressLabel, "text", "updateFieldsSignal()"); + registerField("selectedAddressField", ui->selectedAddress, "text", "updateFieldsSignal()"); + + //This is used to control the disable/enable of the next button on this page. + registerField("isCompleteSelectDestination*", ui->isCompleteCheckBox); +} + +ConsolidateUnspentWizardSelectDestinationPage::~ConsolidateUnspentWizardSelectDestinationPage() +{ + delete ui; +} + +void ConsolidateUnspentWizardSelectDestinationPage::initializePage() +{ + // This fills the selected address fields out again if this page is reaccessed by back and then next + // from the select inputs page without any changes. + if (m_selectedDestinationAddress.first.size()) + { + ui->selectedAddressLabel->setText(m_selectedDestinationAddress.first); + ui->selectedAddress->setText(m_selectedDestinationAddress.second); + + ui->isCompleteCheckBox->setChecked(true); + } +} + +// ----------------------------------------------------------------------- address - label +void ConsolidateUnspentWizardSelectDestinationPage::SetAddressList(std::map addressList) +{ + ui->addressTableWidget->setSortingEnabled(false); + + ui->addressTableWidget->setRowCount(addressList.size()); + + int row = 0; + for (const auto& iter : addressList) + { + QTableWidgetItem* label = new QTableWidgetItem(iter.second); + QTableWidgetItem* address = new QTableWidgetItem(iter.first); + + if (label != nullptr) ui->addressTableWidget->setItem(row, 0, label); + if (address != nullptr) ui->addressTableWidget->setItem(row, 1, address); + + ++row; + } + + ui->addressTableWidget->setSortingEnabled(true); +} + +void ConsolidateUnspentWizardSelectDestinationPage::setDefaultAddressSelection(QString address) +{ + if (!address.size()) + { + ui->addressTableWidget->clearSelection(); + + m_selectedDestinationAddress = {}; + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: Cleared (default) address selection", __func__); + + ui->isCompleteCheckBox->setChecked(false); + + return; + } + + QList defaultAddress = ui->addressTableWidget->findItems(address, Qt::MatchExactly); + + defaultAddress[0]->setSelected(true); + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: Set default address to %s, QTableWidgetItem %s", + __func__, + address.toStdString(), + defaultAddress[0]->text().toStdString()); + + ui->addressTableWidget->setCurrentItem(defaultAddress[0]); + + LogPrintf("INFO: %s: currentRow = %i", __func__, ui->addressTableWidget->currentRow()); + + emit updateFieldsSignal(); +} + +void ConsolidateUnspentWizardSelectDestinationPage::addressSelectionChanged() +{ + + if (!ui->addressTableWidget->selectedItems().size()) + { + ui->selectedAddressLabel->setText(QString()); + ui->selectedAddress->setText(QString()); + + ui->isCompleteCheckBox->setChecked(false); + + return; + } + + ui->addressTableWidget->selectedItems()[0]->row(); + + int selectedRow = ui->addressTableWidget->selectedItems()[0]->row(); + + if (selectedRow < 0) return; + + QTableWidgetItem* selectedLabel = ui->addressTableWidget->item(selectedRow, 0); + QTableWidgetItem* selectedAddress = ui->addressTableWidget->item(selectedRow, 1); + + ui->selectedAddressLabel->setText(selectedLabel->text()); + ui->selectedAddress->setText(selectedAddress->text()); + + m_selectedDestinationAddress = std::make_pair(selectedLabel->text(), selectedAddress->text()); + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: Label %, Address %s selected.", __func__, + m_selectedDestinationAddress.first.toStdString(), + m_selectedDestinationAddress.second.toStdString()); + + ui->isCompleteCheckBox->setChecked(true); + + emit updateFieldsSignal(); +} diff --git a/src/qt/consolidateunspentwizardselectdestinationpage.h b/src/qt/consolidateunspentwizardselectdestinationpage.h new file mode 100644 index 0000000000..621c773557 --- /dev/null +++ b/src/qt/consolidateunspentwizardselectdestinationpage.h @@ -0,0 +1,36 @@ +#ifndef CONSOLIDATEUNSPENTWIZARDSELECTDESTINATIONPAGE_H +#define CONSOLIDATEUNSPENTWIZARDSELECTDESTINATIONPAGE_H + +#include + +namespace Ui { + class ConsolidateUnspentWizardSelectDestinationPage; +} + +class ConsolidateUnspentWizardSelectDestinationPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit ConsolidateUnspentWizardSelectDestinationPage(QWidget *parent = nullptr); + ~ConsolidateUnspentWizardSelectDestinationPage(); + + void initializePage(); + +signals: + void updateFieldsSignal(); + +public slots: + void SetAddressList(std::map addressList); + void setDefaultAddressSelection(QString address); + +private: + Ui::ConsolidateUnspentWizardSelectDestinationPage *ui; + + std::pair m_selectedDestinationAddress; + +private slots: + void addressSelectionChanged(); +}; + +#endif // CONSOLIDATEUNSPENTWIZARDSELECTDESTINATIONPAGE_H diff --git a/src/qt/consolidateunspentwizardselectinputspage.cpp b/src/qt/consolidateunspentwizardselectinputspage.cpp new file mode 100644 index 0000000000..b2e1a050fd --- /dev/null +++ b/src/qt/consolidateunspentwizardselectinputspage.cpp @@ -0,0 +1,719 @@ +#include "coincontroldialog.h" +#include "consolidateunspentwizardselectinputspage.h" +#include "ui_consolidateunspentwizardselectinputspage.h" + +#include "init.h" +#include "bitcoinunits.h" +#include "addresstablemodel.h" +#include "optionsmodel.h" +#include "policy/policy.h" +#include "policy/fees.h" +#include "validation.h" +#include "wallet/coincontrol.h" +#include "consolidateunspentdialog.h" + +using namespace std; + +ConsolidateUnspentWizardSelectInputsPage::ConsolidateUnspentWizardSelectInputsPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::ConsolidateUnspentWizardSelectInputsPage) +{ + m_InputSelectionLimit = GetMaxInputsForConsolidationTxn(); + + ui->setupUi(this); + + // toggle tree/list mode + connect(ui->treeModeRadioButton, SIGNAL(toggled(bool)), this, SLOT(treeModeRadioButton(bool))); + connect(ui->listModeRadioButton, SIGNAL(toggled(bool)), this, SLOT(listModeRadioButton(bool))); + + // click on checkbox + connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int))); + + // click on header + ui->treeWidget->header()->setSectionsClickable(true); + connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int))); + + // (un)select all + connect(ui->selectAllPushButton, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked())); + + // filter/consolidate button interaction + connect(ui->maxMinOutputValue, SIGNAL(textChanged()), this, SLOT(maxMinOutputValueChanged())); + + // filter mode + connect(ui->filterModePushButton, SIGNAL(clicked()), this, SLOT(buttonFilterModeClicked())); + + // filter + connect(ui->filterPushButton, SIGNAL(clicked()), this, SLOT(buttonFilterClicked())); + + ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 150); + ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 170); + ui->treeWidget->setColumnWidth(COLUMN_LABEL, 200); + ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290); + ui->treeWidget->setColumnWidth(COLUMN_DATE, 110); + ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100); + ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100); + ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64_t in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64_t in this column, but don't show it + ui->treeWidget->setColumnHidden(COLUMN_CHANGE_BOOL, true); // store change flag but don't show it + + // This is to provide a convenient way to populate the fields shown on the last page ("send" screen). + registerField("quantityField", ui->quantityLabel, "text", "updateFieldsSignal()"); + registerField("feeField", ui->feeLabel, "text", "updateFieldsSignal()"); + registerField("afterFeeAmountField", ui->afterFeeLabel, "text", "updateFieldsSignal()"); + + //This is used to control the disable/enable of the next button on this page. + registerField("isCompleteSelectInputs*", ui->isCompleteCheckBox); + + // default view is sorted by amount desc + sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder); + + ui->outputLimitWarningIconLabel->setToolTip(tr("Note: The number of inputs selected for consolidation has been " + "limited to %1 to prevent a transaction failure due to too many " + "inputs.").arg(m_InputSelectionLimit)); + ui->outputLimitStopIconLabel->setToolTip(tr("Note: The number of inputs selected for consolidation is currently more " + "than the limit of %1. Please use the filter or manual selection to reduce " + "the number of inputs to %1 or less to prevent a transaction failure due to " + "too many inputs.").arg(m_InputSelectionLimit)); + + ui->outputLimitWarningIconLabel->setVisible(false); + ui->outputLimitStopIconLabel->setVisible(false); + + ui->isCompleteCheckBox->hide(); +} + +ConsolidateUnspentWizardSelectInputsPage::~ConsolidateUnspentWizardSelectInputsPage() +{ + delete ui; +} + +void ConsolidateUnspentWizardSelectInputsPage::setModel(WalletModel *model) +{ + this->model = model; + + if (model && model->getOptionsModel() && model->getAddressTableModel() && coinControl != nullptr) + { + updateView(); + updateLabels(); + } +} + +void ConsolidateUnspentWizardSelectInputsPage::setCoinControl(CCoinControl *coinControl) +{ + this->coinControl = coinControl; +} + +void ConsolidateUnspentWizardSelectInputsPage::setPayAmounts(QList *payAmounts) +{ + this->payAmounts = payAmounts; +} + +// helper function str_pad +QString ConsolidateUnspentWizardSelectInputsPage::strPad(QString s, int nPadLength, QString sPadding) +{ + while (s.length() < nPadLength) + s = sPadding + s; + + return s; +} + +// (un)select all +void ConsolidateUnspentWizardSelectInputsPage::buttonSelectAllClicked() +{ + m_InputSelectionLimitedByFilter = false; + + ui->treeWidget->setEnabled(false); + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != m_ToState) + ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, m_ToState); + ui->treeWidget->setEnabled(true); + + if (m_ToState == Qt::Checked) + { + m_ToState = Qt::Unchecked; + } + else + { + m_ToState = Qt::Checked; + } + + if (m_ToState == Qt::Checked) + { + ui->selectAllPushButton->setText("Select All"); + } + else + { + ui->selectAllPushButton->setText("Select None"); + } + + updateLabels(); +} + +void ConsolidateUnspentWizardSelectInputsPage::maxMinOutputValueChanged() +{ + ui->maxMinOutputValue->value(&m_FilterValueValid); +} + +void ConsolidateUnspentWizardSelectInputsPage::buttonFilterModeClicked() +{ + if (m_FilterMode) + { + m_FilterMode = false; + ui->filterModePushButton->setText(">="); + } + else + { + m_FilterMode = true; + ui->filterModePushButton->setText("<="); + } +} + +void ConsolidateUnspentWizardSelectInputsPage::buttonFilterClicked() +{ + m_ViewItemsChangedViaFilter = true; + + m_InputSelectionLimitedByFilter = filterInputsByValue(m_FilterMode, ui->maxMinOutputValue->value(), m_InputSelectionLimit); + + updateLabels(); + + m_ViewItemsChangedViaFilter = false; +} + +bool ConsolidateUnspentWizardSelectInputsPage::filterInputsByValue(const bool& less, const CAmount& inputFilterValue, + const unsigned int& inputSelectionLimit) +{ + + // Disable generating update signals unnecessarily during this filter operation. + ui->treeWidget->setEnabled(false); + + QTreeWidgetItemIterator iter(ui->treeWidget); + + // If less is true, then we are choosing the smallest inputs upward, and so the map comparator needs to be "less than". + // If less is false, then we are choosing the largest inputs downward, and so the map comparator needs to be "greater + // than". + auto comp = [less](CAmount a, CAmount b) + { + if (less) + { + return (a < b); + } + else + { + return (a > b); + } + }; + + std::multimap, decltype(comp)> input_map(comp); + + bool culled_inputs = false; + + while (*iter) + { + CAmount input_value = (*iter)->text(COLUMN_AMOUNT_INT64).toLongLong(); + COutPoint outpoint(uint256S((*iter)->text(COLUMN_TXHASH).toStdString()), (*iter)->text(COLUMN_VOUT_INDEX).toUInt()); + + if ((*iter)->checkState(COLUMN_CHECKBOX) == Qt::Checked) + { + if ((*iter)->text(COLUMN_TXHASH).length() == 64) + { + if ((less && input_value <= inputFilterValue) || (!less && input_value >= inputFilterValue)) + { + input_map.insert(std::make_pair(input_value, std::make_pair(*iter, outpoint))); + } + else + { + (*iter)->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + coinControl->UnSelect(outpoint); + } + } + } + + ++iter; + } + + // The second loop is to limit the number of selected outputs to the inputCountLimit. + unsigned int input_count = 0; + + for (auto& input : input_map) + { + if (input_count >= inputSelectionLimit) + { + LogPrint(BCLog::LogFlags::QT, "INFO: %s: Culled input %u with value %f.", + __func__, input_count, (double) input.first / COIN); + + if (coinControl->IsSelected(input.second.second.hash, input.second.second.n)) + { + input.second.first->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + culled_inputs = true; + coinControl->UnSelect(input.second.second); + } + } + + ++input_count; + } + + // Reenable update signals. + ui->treeWidget->setEnabled(true); + + // If the number of inputs selected was limited, then true is returned. + return culled_inputs; +} + +// treeview: sort +void ConsolidateUnspentWizardSelectInputsPage::sortView(int column, Qt::SortOrder order) +{ + sortColumn = column; + sortOrder = order; + ui->treeWidget->sortItems(column, order); + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? + COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? + COLUMN_PRIORITY : sortColumn)), + sortOrder); +} + +// treeview: clicked on header +void ConsolidateUnspentWizardSelectInputsPage::headerSectionClicked(int logicalIndex) +{ + if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing + { + ui->treeWidget->header()->setSortIndicator((sortColumn == COLUMN_AMOUNT_INT64 ? + COLUMN_AMOUNT : (sortColumn == COLUMN_PRIORITY_INT64 ? + COLUMN_PRIORITY : sortColumn)), + sortOrder); + } + else + { + if (logicalIndex == COLUMN_AMOUNT) // sort by amount + logicalIndex = COLUMN_AMOUNT_INT64; + + if (logicalIndex == COLUMN_PRIORITY) // sort by priority + logicalIndex = COLUMN_PRIORITY_INT64; + + if (sortColumn == logicalIndex) + sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder); + else + { + sortColumn = logicalIndex; + + // if amount,date,conf,priority then default => desc, else default => asc + sortOrder = ((sortColumn == COLUMN_AMOUNT_INT64 || sortColumn == COLUMN_PRIORITY_INT64 + || sortColumn == COLUMN_DATE || sortColumn == COLUMN_CONFIRMATIONS) ? + Qt::DescendingOrder : Qt::AscendingOrder); + } + + sortView(sortColumn, sortOrder); + } +} + + +// toggle tree mode +void ConsolidateUnspentWizardSelectInputsPage::treeModeRadioButton(bool checked) +{ + if (checked && model) + updateView(); +} + +// toggle list mode +void ConsolidateUnspentWizardSelectInputsPage::listModeRadioButton(bool checked) +{ + if (checked && model) + updateView(); +} + +// checkbox clicked by user +void ConsolidateUnspentWizardSelectInputsPage::viewItemChanged(QTreeWidgetItem* item, int column) +{ + if (!m_ViewItemsChangedViaFilter) m_InputSelectionLimitedByFilter = false; + + if (column == COLUMN_CHECKBOX) + { + // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) + if (item->text(COLUMN_TXHASH).length() == 64) + { + COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()); + + if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) + { + coinControl->UnSelect(outpt); + } + else if (item->isDisabled()) // locked (this happens if "check all" through parent node) + { + item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + } + else + { + coinControl->Select(outpt); + } + } + + // selection changed -> update labels + if (ui->treeWidget->isEnabled()) + { + // do not update on every click for (un)select all + updateLabels(); + } + } +} + +void ConsolidateUnspentWizardSelectInputsPage::updateLabels() +{ + if (!model) return; + + // nPayAmount + qint64 nPayAmount = 0; + CTransaction txDummy; + for (const auto& amount: *payAmounts) + { + nPayAmount += amount; + + if (amount > 0) + { + CTxOut txout(amount, (CScript)vector(24, 0)); + txDummy.vout.push_back(txout); + } + } + + QString sPriorityLabel = QString(); + int64_t nAmount = 0; + int64_t nPayFee = 0; + int64_t nAfterFee = 0; + int64_t nChange = 0; + unsigned int nBytes = 0; + unsigned int nBytesInputs = 0; + unsigned int nQuantity = 0; + + vector vCoinControl; + vector vOutputs; + coinControl->ListSelected(vCoinControl); + model->getOutputs(vCoinControl, vOutputs); + + for (const auto& out : vOutputs) + { + // Quantity + nQuantity++; + + // Amount + nAmount += out.tx->vout[out.i].nValue; + + // Bytes + CTxDestination address; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + { + CPubKey pubkey; + try { + if (model->getPubKey(std::get(address), pubkey)) + nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); + else + nBytesInputs += 148; // in all error cases, simply assume 148 here + } catch (const std::bad_variant_access&) { + nBytesInputs += 148; + } + } + else nBytesInputs += 148; + } + + // calculation + if (nQuantity > 0) + { + // Bytes - always assume +1 output for change here + nBytes = nBytesInputs + ((payAmounts->size() > 0 ? payAmounts->size() + 1 : 2) * 34) + 10; + + // Fee + int64_t nFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); + + // Min Fee + int64_t nMinFee = GetMinFee(txDummy, 1000, GMF_SEND, nBytes); + + nPayFee = max(nFee, nMinFee); + + if (nPayAmount > 0) + { + nChange = nAmount - nPayFee - nPayAmount; + + // if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee + if (nPayFee < CENT && nChange > 0 && nChange < CENT) + { + if (nChange < CENT) // change < 0.01 => simply move all change to fees + { + nPayFee = nChange; + nChange = 0; + } + else + { + nChange = nChange + nPayFee - CENT; + nPayFee = CENT; + } + } + + if (nChange == 0) nBytes -= 34; + } + + // after fee + nAfterFee = nAmount - nPayFee; + if (nAfterFee < 0) nAfterFee = 0; + } + + // actually update labels + int nDisplayUnit = BitcoinUnits::BTC; + if (model && model->getOptionsModel()) nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + + // stats + ui->quantityLabel->setText(QString::number(nQuantity)); // Quantity + ui->feeLabel->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee + ui->afterFeeLabel->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee + + std::map addressList; + QString defaultAddress; + unsigned int numberAddressesWhereOutputsChecked = 0; + + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) + { + QString label = ui->treeWidget->topLevelItem(i)->text(COLUMN_LABEL); + QString address = ui->treeWidget->topLevelItem(i)->text(COLUMN_ADDRESS); + QString change = ui->treeWidget-> topLevelItem(i)->text(COLUMN_CHANGE_BOOL); + + Qt::CheckState state = ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX); + + // If a not unchecked top level item is not a change address and it results in an insert into the m_AddressList + if (!change.toInt() && addressList.insert(std::make_pair(address, label)).second) + { + if (state == Qt::Checked || state == Qt::PartiallyChecked) + { + defaultAddress = label; + + ++numberAddressesWhereOutputsChecked; + } + } + } + + if (!addressList.empty()) emit setAddressListSignal(addressList); + + // This covers the 0 case too, where the default address will be an empty QString. + if (numberAddressesWhereOutputsChecked < 2) + { + // This will be an empty QString if the numberAddressesWhereOutputsChecked equals 0. It will be + // the above defaultAddress if numberAddressesWhereOutputsChecked equals 1. + emit setDefaultAddressSignal(defaultAddress); + } + else + { + // If numberAddressesWhereOutputsChecked is 2 or greater, then clear the default address (i.e. set to + // empty QString. + emit setDefaultAddressSignal(QString()); + } + + // This provids the trigger to update the fields from the labels, since they are QLabels and don't have appropriate + // internal signals. + emit updateFieldsSignal(); + + if (nQuantity < 2) + { + SetOutputWarningStop(InputStatus::INSUFFICIENT_OUTPUTS); + } + else if (nQuantity < m_InputSelectionLimit + || (nQuantity == m_InputSelectionLimit && !m_InputSelectionLimitedByFilter)) + { + SetOutputWarningStop(InputStatus::NORMAL); + } + else if (nQuantity == m_InputSelectionLimit && m_InputSelectionLimitedByFilter) + { + SetOutputWarningStop(InputStatus::WARNING); + } + else if (nQuantity > m_InputSelectionLimit) + { + SetOutputWarningStop(InputStatus::STOP); + } +} + +void ConsolidateUnspentWizardSelectInputsPage::updateView() +{ + bool treeMode = ui->treeModeRadioButton->isChecked(); + + ui->treeWidget->clear(); + ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox + ui->treeWidget->setAlternatingRowColors(!treeMode); + QFlags flgCheckbox=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + QFlags flgTristate=Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; + + int nDisplayUnit = BitcoinUnits::BTC; + + if (model && model->getOptionsModel()) + { + nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + } + + map> mapCoins; + model->listCoins(mapCoins); + + for (auto const& coins : mapCoins) + { + QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem(); + QString sWalletAddress = coins.first; + QString sWalletLabel = ""; + if (model->getAddressTableModel()) + sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); + if (sWalletLabel.length() == 0) + sWalletLabel = tr("(no label)"); + + if (treeMode) + { + // wallet address + ui->treeWidget->addTopLevelItem(itemWalletAddress); + + itemWalletAddress->setFlags(flgTristate); + itemWalletAddress->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + // label + itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); + + // address + itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); + } + + int64_t nSum = 0; + double dPrioritySum = 0; + int nChildren = 0; + int nInputSum = 0; + + for (auto const& out : coins.second) + { + int nInputSize = 148; // 180 if uncompressed public key + nSum += out.tx->vout[out.i].nValue; + nChildren++; + + QTreeWidgetItem *itemOutput; + if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress); + else itemOutput = new QTreeWidgetItem(ui->treeWidget); + itemOutput->setFlags(flgCheckbox); + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); + + // address + CTxDestination outputAddress; + QString sAddress = ""; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) + { + sAddress = CBitcoinAddress(outputAddress).ToString().c_str(); + + // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs + if (!treeMode || (!(sAddress == sWalletAddress))) + itemOutput->setText(COLUMN_ADDRESS, sAddress); + + CPubKey pubkey; + try { + if (model->getPubKey(std::get(outputAddress), pubkey) && !pubkey.IsCompressed()) + nInputSize = 180; + } catch (const std::bad_variant_access&) {} + } + + // label + if (!(sAddress == sWalletAddress)) // change + { + // tooltip from where the change comes from + itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); + itemOutput->setText(COLUMN_LABEL, tr("(change)")); + itemOutput->setText(COLUMN_CHANGE_BOOL, QString::number(1)); + } + else if (!treeMode) + { + QString sLabel = ""; + if (model->getAddressTableModel()) + sLabel = model->getAddressTableModel()->labelForAddress(sAddress); + if (sLabel.length() == 0) + sLabel = tr("(no label)"); + itemOutput->setText(COLUMN_LABEL, sLabel); + } + + // amount + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); + itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly + + // date + itemOutput->setText(COLUMN_DATE, QDateTime::fromTime_t(out.tx->GetTxTime()).toUTC().toString("yy-MM-dd hh:mm")); + + // immature PoS reward + { + // LOCK on cs_main must be taken for depth and maturity. + LOCK(cs_main); + + if (out.tx->IsCoinStake() && out.tx->GetBlocksToMaturity() > 0 && out.tx->GetDepthInMainChain() > 0) { + itemOutput->setBackground(COLUMN_CONFIRMATIONS, Qt::red); + itemOutput->setDisabled(true); + } + } + + // confirmations + itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); + + // priority + double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 + itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority)); + itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " ")); + dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + nInputSum += nInputSize; + + // transaction hash + uint256 txhash = out.tx->GetHash(); + itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str()); + + // vout index + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + + // set checkbox + if (coinControl->IsSelected(txhash, out.i)) + { + itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Checked); + } + } + + // amount + if (treeMode) + { + dPrioritySum = dPrioritySum / (nInputSum + 78); + itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); + itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); + itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); + itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum)); + itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " ")); + } + } + + // expand all partially selected + if (treeMode) + { + for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) + ui->treeWidget->topLevelItem(i)->setExpanded(true); + } + + // sort view + sortView(sortColumn, sortOrder); + ui->treeWidget->setEnabled(true); +} + +void ConsolidateUnspentWizardSelectInputsPage::SetOutputWarningStop(InputStatus input_status) +{ + switch (input_status) + { + case InputStatus::INSUFFICIENT_OUTPUTS: + ui->outputLimitWarningIconLabel->setVisible(false); + ui->outputLimitStopIconLabel->setVisible(false); + ui->isCompleteCheckBox->setChecked(false); + break; + case InputStatus::NORMAL: + ui->outputLimitWarningIconLabel->setVisible(false); + ui->outputLimitStopIconLabel->setVisible(false); + ui->isCompleteCheckBox->setChecked(true); + break; + case InputStatus::WARNING: + ui->outputLimitWarningIconLabel->setVisible(true); + ui->outputLimitStopIconLabel->setVisible(false); + ui->isCompleteCheckBox->setChecked(true); + break; + case InputStatus::STOP: + ui->outputLimitWarningIconLabel->setVisible(false); + ui->outputLimitStopIconLabel->setVisible(true); + ui->isCompleteCheckBox->setChecked(false); + } +} diff --git a/src/qt/consolidateunspentwizardselectinputspage.h b/src/qt/consolidateunspentwizardselectinputspage.h new file mode 100644 index 0000000000..439d5b0b5c --- /dev/null +++ b/src/qt/consolidateunspentwizardselectinputspage.h @@ -0,0 +1,91 @@ +#ifndef CONSOLIDATEUNSPENTWIZARDSELECTINPUTS_H +#define CONSOLIDATEUNSPENTWIZARDSELECTINPUTS_H + +#include "walletmodel.h" +#include "amount.h" + +#include +#include + +namespace Ui { + class ConsolidateUnspentWizardSelectInputsPage; +} + +class CoinControlDialog; + +class ConsolidateUnspentWizardSelectInputsPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit ConsolidateUnspentWizardSelectInputsPage(QWidget *parent = nullptr); + ~ConsolidateUnspentWizardSelectInputsPage(); + + void setModel(WalletModel*); + void setCoinControl(CCoinControl* coinControl); + void setPayAmounts(QList *payAmounts); + +signals: + void setAddressListSignal(std::map); + void setDefaultAddressSignal(QString); + void updateFieldsSignal(); + +public slots: + void updateLabels(); + +private: + Ui::ConsolidateUnspentWizardSelectInputsPage *ui; + CCoinControl *coinControl; + QList *payAmounts; + WalletModel *model; + int sortColumn; + Qt::SortOrder sortOrder; + size_t m_InputSelectionLimit; + Qt::CheckState m_ToState = Qt::Checked; + bool m_FilterMode = true; + bool m_FilterValueValid = false; + bool m_InputSelectionLimitedByFilter = false; + bool m_ViewItemsChangedViaFilter = false; + + QString strPad(QString, int, QString); + void sortView(int, Qt::SortOrder); + void updateView(); + bool filterInputsByValue(const bool& less, const CAmount& inputFilterValue, const unsigned int& inputSelectionLimit); + + enum + { + COLUMN_CHECKBOX, + COLUMN_AMOUNT, + COLUMN_LABEL, + COLUMN_ADDRESS, + COLUMN_DATE, + COLUMN_CONFIRMATIONS, + COLUMN_PRIORITY, + COLUMN_TXHASH, + COLUMN_VOUT_INDEX, + COLUMN_AMOUNT_INT64, + COLUMN_PRIORITY_INT64, + COLUMN_CHANGE_BOOL + }; + + enum InputStatus + { + INSUFFICIENT_OUTPUTS, + NORMAL, + WARNING, + STOP + }; + +private slots: + void treeModeRadioButton(bool); + void listModeRadioButton(bool); + void viewItemChanged(QTreeWidgetItem*, int); + void headerSectionClicked(int); + void buttonSelectAllClicked(); + void maxMinOutputValueChanged(); + void buttonFilterModeClicked(); + void buttonFilterClicked(); + void SetOutputWarningStop(InputStatus input_status); +}; + +#endif // CONSOLIDATEUNSPENTWIZARDSELECTINPUTS_H diff --git a/src/qt/consolidateunspentwizardsendpage.cpp b/src/qt/consolidateunspentwizardsendpage.cpp new file mode 100644 index 0000000000..2a50e3e080 --- /dev/null +++ b/src/qt/consolidateunspentwizardsendpage.cpp @@ -0,0 +1,53 @@ +#include "consolidateunspentwizardsendpage.h" +#include "ui_consolidateunspentwizardsendpage.h" + +#include "util.h" +#include "bitcoinunits.h" +#include "optionsmodel.h" + +ConsolidateUnspentWizardSendPage::ConsolidateUnspentWizardSendPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::ConsolidateUnspentWizardSendPage) +{ + ui->setupUi(this); +} + +ConsolidateUnspentWizardSendPage::~ConsolidateUnspentWizardSendPage() +{ + delete ui; +} + +void ConsolidateUnspentWizardSendPage::initializePage() +{ + ui->InputQuantityLabel->setText(field("quantityField").toString()); + ui->feeLabel->setText(field("feeField").toString()); + ui->afterFeeAmountLabel->setText(field("afterFeeAmountField").toString()); + ui->destinationAddressLabelLabel->setText(field("selectedAddressLabelField").toString()); + ui->destinationAddressLabel->setText(field("selectedAddressField").toString()); + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: destinationAddress = %s", + __func__, field("selectedAddressField").toString().toStdString()); + + qint64 amount = 0; + bool parse_status = false; + + m_recipient.label = ui->destinationAddressLabelLabel->text(); + m_recipient.address = ui->destinationAddressLabel->text(); + + parse_status = BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), + ui->afterFeeAmountLabel->text() + .left(ui->afterFeeAmountLabel->text().indexOf(" ")), + &amount); + + if (parse_status) m_recipient.amount = amount; +} + +void ConsolidateUnspentWizardSendPage::setModel(WalletModel *model) +{ + this->model = model; +} + +void ConsolidateUnspentWizardSendPage::onFinishButtonClicked() +{ + emit selectedConsolidationRecipientSignal(m_recipient); +} diff --git a/src/qt/consolidateunspentwizardsendpage.h b/src/qt/consolidateunspentwizardsendpage.h new file mode 100644 index 0000000000..344bacc67c --- /dev/null +++ b/src/qt/consolidateunspentwizardsendpage.h @@ -0,0 +1,39 @@ +#ifndef CONSOLIDATEUNSPENTWIZARDSENDPAGE_H +#define CONSOLIDATEUNSPENTWIZARDSENDPAGE_H + +#include "walletmodel.h" +#include "amount.h" + +#include + +namespace Ui { + class ConsolidateUnspentWizardSendPage; +} + +class ConsolidateUnspentWizardSendPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit ConsolidateUnspentWizardSendPage(QWidget *parent = nullptr); + ~ConsolidateUnspentWizardSendPage(); + + void initializePage(); + + void setModel(WalletModel*); + +public slots: + void onFinishButtonClicked(); + +signals: + void selectedConsolidationRecipientSignal(SendCoinsRecipient consolidationRecipient); + +private: + Ui::ConsolidateUnspentWizardSendPage *ui; + WalletModel *model; + SendCoinsRecipient m_recipient; + + size_t m_inputSelectionLimit; +}; + +#endif // CONSOLIDATEUNSPENTWIZARDSENDPAGE_H diff --git a/src/qt/forms/consolidateunspentwizard.ui b/src/qt/forms/consolidateunspentwizard.ui new file mode 100644 index 0000000000..ddde9fb2d2 --- /dev/null +++ b/src/qt/forms/consolidateunspentwizard.ui @@ -0,0 +1,69 @@ + + + ConsolidateUnspentWizard + + + + 0 + 0 + 933 + 700 + + + + + 0 + 0 + + + + Conolidate Unspent Transaction Outputs (UTXOs) + + + true + + + true + + + QWizard::ClassicStyle + + + + 0 + + + + + 1 + + + + + 2 + + + + + + ConsolidateUnspentWizardSelectInputsPage + QWizardPage +
consolidateunspentwizardselectinputspage.h
+ 1 +
+ + ConsolidateUnspentWizardSelectDestinationPage + QWizardPage +
consolidateunspentwizardselectdestinationpage.h
+ 1 +
+ + ConsolidateUnspentWizardSendPage + QWizardPage +
consolidateunspentwizardsendpage.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/consolidateunspentwizardselectdestinationpage.ui b/src/qt/forms/consolidateunspentwizardselectdestinationpage.ui new file mode 100644 index 0000000000..c7486abf21 --- /dev/null +++ b/src/qt/forms/consolidateunspentwizardselectdestinationpage.ui @@ -0,0 +1,132 @@ + + + ConsolidateUnspentWizardSelectDestinationPage + + + + 0 + 0 + 900 + 700 + + + + WizardPage + + + + + 19 + 19 + 851 + 581 + + + + + + + Step 2: Select the destination address for the consolidation transaction. Note that all of the selected inputs will be consolidated to an output on this address. If there is a very small amount of change (due to uncertainty in the fee calculation), it will also be sent to this address. If you selected inputs only from a particular address on the previous page, then that address will already be selected by default. + + + true + + + + + + + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + 90 + + + true + + + false + + + + Label + + + + + Address + + + + + + + + + + + + Currently selected: + + + + + + + + + + + Label + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Address + + + + + + + + + isComplete + + + + + + + + + diff --git a/src/qt/forms/consolidateunspentwizardselectinputspage.ui b/src/qt/forms/consolidateunspentwizardselectinputspage.ui new file mode 100644 index 0000000000..bd719917df --- /dev/null +++ b/src/qt/forms/consolidateunspentwizardselectinputspage.ui @@ -0,0 +1,371 @@ + + + ConsolidateUnspentWizardSelectInputsPage + + + + 0 + 0 + 900 + 700 + + + + WizardPage + + + + + 20 + 20 + 851 + 581 + + + + + + + + + + 0 + 0 + + + + Step 1: Select the inputs to be consolidated. Remember that the inputs to the consolidation are your unspent outputs (UTXOs) in your wallet. + + + true + + + + + + + + + + + Select All + + + + + + + Tree Mode + + + true + + + + + + + List Mode + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Select inputs + + + + + + + + + + <= + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + Filters the already selected inputs. + + + Filter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::CustomContextMenu + + + false + + + 11 + + + true + + + false + + + + + + + + + Amount + + + + + Label + + + + + Address + + + + + Date + + + + + Confirmations + + + Confirmed + + + + + Priority + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :/icons/warning + + + true + + + + + + + + 64 + 64 + + + + + + + :/icons/white_and_red_x + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Quantity + + + + + + + 99999 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Fee + + + + + + + 99.9999 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + After Fee Amount + + + + + + + 999999999.9999 + + + + + + + isComplete + + + + + + + + + + + CoinControlTreeWidget + QTreeWidget +
coincontroltreewidget.h
+
+ + BitcoinAmountField + QSpinBox +
bitcoinamountfield.h
+ 1 +
+
+ + + + +
diff --git a/src/qt/forms/consolidateunspentwizardsendpage.ui b/src/qt/forms/consolidateunspentwizardsendpage.ui new file mode 100644 index 0000000000..c0ce6e79b1 --- /dev/null +++ b/src/qt/forms/consolidateunspentwizardsendpage.ui @@ -0,0 +1,119 @@ + + + ConsolidateUnspentWizardSendPage + + + + 0 + 0 + 900 + 700 + + + + WizardPage + + + + + 19 + 19 + 851 + 581 + + + + + + + + + Step 3: Confirm Consolidation Transaction Details. Transaction will be ready to send when Finish is pressed. + + + true + + + + + + + + + + + Number of Inputs + + + + + + + 999999 + + + + + + + Transaction Fee + + + + + + + 99.9999 + + + + + + + Amount + + + + + + + 999999999.9999 + + + + + + + Destination Address + + + + + + + address + + + + + + + Destination Address Label + + + + + + + label + + + + + + + + + + + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 3478b8fb4f..17004d7df1 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -85,7 +85,7 @@ - + 8 @@ -129,6 +129,13 @@ + + + + Consolidate Wizard + + + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 09c742b2b0..10c296e4cf 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -12,7 +12,10 @@ #include "askpassphrasedialog.h" #include "wallet/coincontrol.h" +#include "policy/policy.h" #include "coincontroldialog.h" +#include "consolidateunspentdialog.h" +#include "consolidateunspentwizard.h" #include #include @@ -23,6 +26,8 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), + coinControl(new CCoinControl), + payAmounts(new QList), model(0) { ui->setupUi(this); @@ -34,6 +39,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : // Coin Control ui->coinControlChangeEdit->setFont(GUIUtil::bitcoinAddressFont()); connect(ui->coinControlPushButton, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); + connect(ui->coinControlConsolidateWizardPushButton, SIGNAL(clicked()), this, SLOT(coinControlConsolidateWizardButtonClicked())); connect(ui->coinControlResetPushButton, SIGNAL(clicked()), this, SLOT(coinControlResetButtonClicked())); connect(ui->coinControlChangeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); connect(ui->coinControlChangeEdit, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &))); @@ -102,6 +108,8 @@ void SendCoinsDialog::setModel(WalletModel *model) SendCoinsDialog::~SendCoinsDialog() { delete ui; + delete coinControl; + delete payAmounts; } void SendCoinsDialog::on_sendButton_clicked() @@ -167,7 +175,7 @@ void SendCoinsDialog::on_sendButton_clicked() if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) sendstatus = model->sendCoins(recipients); else - sendstatus = model->sendCoins(recipients, CoinControlDialog::coinControl); + sendstatus = model->sendCoins(recipients, coinControl); switch(sendstatus.status) { @@ -211,7 +219,7 @@ void SendCoinsDialog::on_sendButton_clicked() break; case WalletModel::OK: accept(); - CoinControlDialog::coinControl->UnSelectAll(); + coinControl->UnSelectAll(); coinControlUpdateLabels(); break; } @@ -423,13 +431,13 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) ui->frameCoinControl->setVisible(checked); if (!checked && model) // coin control features disabled - CoinControlDialog::coinControl->SetNull(); + coinControl->SetNull(); } // Coin Control: button inputs -> show actual coin control dialog void SendCoinsDialog::coinControlButtonClicked() { - CoinControlDialog dlg; + CoinControlDialog dlg(this, coinControl, payAmounts); dlg.setModel(model); connect(&dlg, SIGNAL(selectedConsolidationRecipientSignal(SendCoinsRecipient)), @@ -441,12 +449,32 @@ void SendCoinsDialog::coinControlButtonClicked() void SendCoinsDialog::coinControlResetButtonClicked() { - CoinControlDialog::coinControl->SetNull(); + coinControl->SetNull(); + coinControlUpdateLabels(); +} + +void SendCoinsDialog::coinControlConsolidateWizardButtonClicked() +{ + CoinControlDialog dlg(this, coinControl, payAmounts); + dlg.setModel(model); + + connect(&dlg, SIGNAL(selectedConsolidationRecipientSignal(SendCoinsRecipient)), + this, SLOT(selectedConsolidationRecipient(SendCoinsRecipient))); + + ConsolidateUnspentWizard wizard(this, coinControl, payAmounts); + wizard.setModel(model); + + connect(&wizard, SIGNAL(selectedConsolidationRecipientSignal(SendCoinsRecipient)), + this, SLOT(selectedConsolidationRecipient(SendCoinsRecipient))); + + wizard.exec(); coinControlUpdateLabels(); } void SendCoinsDialog::selectedConsolidationRecipient(SendCoinsRecipient consolidationRecipient) { + LogPrintf("INFO: %s: SLOT called.", __func__); + ui->coinControlChangeCheckBox->setChecked(true); ui->coinControlChangeEdit->setText(consolidationRecipient.address); @@ -469,9 +497,9 @@ void SendCoinsDialog::coinControlChangeChecked(int state) if (model) { if (state == Qt::Checked) - CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->coinControlChangeEdit->text().toStdString()).Get(); + coinControl->destChange = CBitcoinAddress(ui->coinControlChangeEdit->text().toStdString()).Get(); else - CoinControlDialog::coinControl->destChange = CNoDestination(); + coinControl->destChange = CNoDestination(); } ui->coinControlChangeEdit->setEnabled((state == Qt::Checked)); @@ -483,7 +511,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString & text) { if (model) { - CoinControlDialog::coinControl->destChange = CBitcoinAddress(text.toStdString()).Get(); + coinControl->destChange = CBitcoinAddress(text.toStdString()).Get(); // label for the change address ui->coinControlChangeLabel->setStyleSheet("QLabel{color:black;}"); @@ -523,18 +551,17 @@ void SendCoinsDialog::coinControlUpdateLabels() return; // set pay amounts - CoinControlDialog::payAmounts.clear(); - for(int i = 0; i < ui->entries->count(); ++i) + payAmounts->clear(); + for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); - if(entry) - CoinControlDialog::payAmounts.append(entry->getValue().amount); + if (entry) payAmounts->append(entry->getValue().amount); } - if (CoinControlDialog::coinControl->HasSelected()) + if (coinControl->HasSelected()) { // actual coin control calculation - CoinControlDialog::updateLabels(model, this); + CoinControlDialog::updateLabels(model, coinControl, payAmounts, this); // show coin control stats ui->coinControlAutomaticallySelectedLabel->hide(); diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 81878ca005..b0479bd9ce 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -43,6 +43,8 @@ public slots: private: Ui::SendCoinsDialog *ui; + CCoinControl *coinControl; + QList *payAmounts; WalletModel *model; bool fNewRecipientAllowed; @@ -53,6 +55,7 @@ private slots: void coinControlFeatureChanged(bool); void coinControlButtonClicked(); void coinControlResetButtonClicked(); + void coinControlConsolidateWizardButtonClicked(); void coinControlChangeChecked(int); void coinControlChangeEdited(const QString &); void coinControlUpdateLabels(); From e9a4a04526dcb88c0b6755ccd5a79108990dcce7 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 8 May 2021 00:55:04 -0400 Subject: [PATCH 097/280] Fix incorrect change field mapping on sendcoinsdialog This corrects the mapping of change to the CoinControlChangeLabel field. --- src/qt/forms/sendcoinsdialog.ui | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 17004d7df1..f100af1374 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -450,7 +450,7 @@ - + IBeamCursor @@ -473,7 +473,7 @@ - + 12 @@ -509,28 +509,6 @@ - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - 3 - - - @@ -561,8 +539,8 @@ 0 0 - 869 - 112 + 879 + 212 From 41313123ec560ca1828fe4f510fc6b15b1d994dd Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 15 May 2021 23:41:52 -0400 Subject: [PATCH 098/280] Fix non virtual destructor on CScraperManifest warning --- src/gridcoin/scraper/scraper_net.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gridcoin/scraper/scraper_net.h b/src/gridcoin/scraper/scraper_net.h index 35d6c2c2e4..572e5aff23 100755 --- a/src/gridcoin/scraper/scraper_net.h +++ b/src/gridcoin/scraper/scraper_net.h @@ -62,7 +62,7 @@ class CSplitBlob int addPartData(CDataStream&& vData); /** Unref all parts referenced by this. Removes parts with no references */ - ~CSplitBlob(); + virtual ~CSplitBlob(); /* We could store the parts in mapRelay and have getdata service for free. */ /** map from part hash to scraper Index, so we can attach incoming Part in Index */ From 924401fbc141802bfb36da5f867ffe5119d5e0bb Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 16 May 2021 00:23:14 -0400 Subject: [PATCH 099/280] Fix clang warnings on critical section cs_vSend in net.h --- src/net.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/net.h b/src/net.h index 38805107e6..c92b516b32 100644 --- a/src/net.h +++ b/src/net.h @@ -459,22 +459,22 @@ class CNode mapAskFor.insert(std::make_pair(nRequestTime, inv)); } + // A lock on cs_vSend must be taken before calling this function void BeginMessage(const char* pszCommand) { - ENTER_CRITICAL_SECTION(cs_vSend); assert(ssSend.size() == 0); ssSend << CMessageHeader(pszCommand, 0); } + // A lock on cs_vSend must be taken before calling this function void AbortMessage() { ssSend.clear(); - LEAVE_CRITICAL_SECTION(cs_vSend); - LogPrint(BCLog::LogFlags::NOISY, "(aborted)"); } + // A lock on cs_vSend must be taken before calling this function void EndMessage() { if (ssSend.size() == 0) @@ -500,8 +500,6 @@ class CNode // If write queue empty, attempt "optimistic write" if (it == vSendMsg.begin()) SocketSendData(this); - - LEAVE_CRITICAL_SECTION(cs_vSend); } void PushVersion(); @@ -521,6 +519,8 @@ class CNode void PushMessage(const char* pszCommand) { + LOCK(cs_vSend); + try { BeginMessage(pszCommand); @@ -536,6 +536,8 @@ class CNode template void PushMessage(const char* pszCommand, Args... args) { + LOCK(cs_vSend); + try { BeginMessage(pszCommand); From e5b2e1f1ecb191a42787fc3cb0f9d15018525a31 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 16 May 2021 18:36:37 -0400 Subject: [PATCH 100/280] Fix warning on QSplashScreen QFlags deprecation --- src/qt/bitcoin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c870ad3972..6970aa4aa0 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -526,7 +526,7 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt return EXIT_FAILURE; } - QSplashScreen splash(QPixmap(":/images/splash"), 0); + QSplashScreen splash(QPixmap(":/images/splash")); if (GetBoolArg("-splash", true) && !GetBoolArg("-min")) { splash.setEnabled(false); From 28573bbbaf328efb98f4e71ddaa99776c58d84de Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 16 May 2021 18:52:16 -0400 Subject: [PATCH 101/280] Fix deprecated Qt::SystemLocaleShortDate --- src/qt/guiutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 144692c1f4..8c65c95b78 100755 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -42,7 +42,7 @@ namespace GUIUtil { QString dateTimeStr(const QDateTime &date) { - return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm"); + return QLocale::system().toString(date, QLocale::ShortFormat) + QString(" ") + date.toString("hh:mm"); } QString dateTimeStr(qint64 nTime) From e0c71db05e2c06909a6664b0ec9f3f2569ef3add Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 16 May 2021 20:03:20 -0400 Subject: [PATCH 102/280] Fix deprecated SignalMapper::mapped for Qt 5.15+ --- src/qt/rpcconsole.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 2df84bbd19..a2059d6a50 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -351,7 +351,12 @@ void RPCConsole::setClientModel(ClientModel *model) connect(banAction24h, &QAction::triggered, signalMapper, static_cast(&QSignalMapper::map)); connect(banAction7d, &QAction::triggered, signalMapper, static_cast(&QSignalMapper::map)); connect(banAction365d, &QAction::triggered, signalMapper, static_cast(&QSignalMapper::map)); + +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(signalMapper, static_cast(&QSignalMapper::mapped), this, &RPCConsole::banSelectedNode); +#else + connect(signalMapper, &QSignalMapper::mappedInt, this, &RPCConsole::banSelectedNode); +#endif // peer table context menu signals connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu); From 6cb959a6869328ff1b43c0917cf20ddcefed7987 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 16 May 2021 20:04:36 -0400 Subject: [PATCH 103/280] Fix deprecated QDateTime(QDate) since Qt 5.14+ --- src/qt/optionsmodel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 3ceb0cd5b2..f33848072b 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -304,7 +304,11 @@ QDate OptionsModel::getLimitTxnDate() int64_t OptionsModel::getLimitTxnDateTime() { +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QDateTime limitTxnDateTime(limitTxnDate); +#else + QDateTime limitTxnDateTime = limitTxnDate.startOfDay(); +#endif return limitTxnDateTime.toMSecsSinceEpoch() / 1000; } From 84c6af057d16ce050a5da54b8b7b1af45c2128f2 Mon Sep 17 00:00:00 2001 From: barton26 Date: Sun, 16 May 2021 23:11:51 -0400 Subject: [PATCH 104/280] Remove CCT from README, add Discord Fixes #2133 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd54c38533..7edf219e1e 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ master if the staging branch is busy. Community ========= -For general questions, see the forum at https://cryptocurrencytalk.com/forum/464-gridcoin-grc/, or Freenode IRC in #gridcoin-help. We also have a Slack channel at [teamgridcoin.slack.com](https://join.slack.com/t/teamgridcoin/shared_invite/zt-3s81akww-GHt~_KvtxfhxUgi3yW3~Bg). +For general questions, please visit our Discord server at https://discord.gg/jf9XX4a, or Freenode IRC in #gridcoin-help. We also have a Slack channel at [teamgridcoin.slack.com](https://join.slack.com/t/teamgridcoin/shared_invite/zt-3s81akww-GHt~_KvtxfhxUgi3yW3~Bg). License ------- From 9b7669e1517630ad3bd270898e44be337dc9b456 Mon Sep 17 00:00:00 2001 From: barton26 Date: Sun, 16 May 2021 23:30:28 -0400 Subject: [PATCH 105/280] Bump Codespell to 2.0.0 See https://github.com/bitcoin/bitcoin/pull/20817/commits/a0022f1cfbb3d8f1f8f3ff135f854be0cb89643f --- ci/lint/04_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index 42633b9cdb..8ca784dc90 100755 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -10,7 +10,7 @@ export LC_ALL=C sudo update-alternatives --install /usr/bin/clang-format clang-format $(which clang-format-9 ) 100 sudo update-alternatives --install /usr/bin/clang-format-diff clang-format-diff $(which clang-format-diff-9) 100 -./ci/retry/retry pip3 install codespell==1.17.1 +./ci/retry/retry pip3 install codespell==2.0.0 ./ci/retry/retry pip3 install flake8==3.8.3 ./ci/retry/retry pip3 install yq ./ci/retry/retry pip3 install mypy==0.781 From a05b8e14c5e4b12dbea78cb0a283d95764202f1c Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Wed, 5 May 2021 21:11:56 -0500 Subject: [PATCH 106/280] Refresh GUI send payment page design This updates the GUI "send payment" page to match the proposed redesign: - Add a header bar with available balance - Limit the max content width for large window sizes - Update colors, layout, sizing, and spacing --- src/qt/forms/sendcoinsdialog.ui | 1291 ++++++++++--------- src/qt/res/stylesheets/dark_stylesheet.qss | 37 +- src/qt/res/stylesheets/light_stylesheet.qss | 34 +- src/qt/sendcoinsdialog.cpp | 21 +- 4 files changed, 786 insertions(+), 597 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index f100af1374..0a6cdecdc6 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -13,12 +13,136 @@ Send Coins - + + + 0 + + + 0 + + + 0 + + + 0 + - 8 + 0 - - + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Send Payment + + + actionButtonsFrame + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0.00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Available (%1) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + 0 @@ -31,12 +155,6 @@ 16777215 - - QFrame::StyledPanel - - - QFrame::Sunken - 6 @@ -51,480 +169,511 @@ 0 - 6 + 0 - + - 0 - - - 10 + 3 - - 10 - - - - - 15 - - - - - - 0 - 0 - - - - Coin Control Features - - - - - - - - - 8 - - - 10 - - - - - - - - Inputs... - - - - - - - automatically selected - - - 5 - - - - - - - Insufficient funds! - - - 5 - - - - - - - Reset - - - - - - - Consolidate Wizard - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - + - + 0 0 - - - 0 - 0 - + + Coin Control Features - - + + + + + + :/icons/round_gray_x - - - 0 + + + + + + Inactive + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 8 - 0 - - - 0 + 10 - 0 + 10 - - - 20 + + + - - 0 + + Inputs... - - 10 + + + + + + automatically selected - - - - 10 - - - 14 - - - 10 - - - 4 - - - 6 - - - - - Quantity: - - - 0 - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - 0 - - - 0 - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Bytes: - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - 0 - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - 10 - - - 14 - - - 6 - - - 4 - - - 6 - - - - - Amount: - - - 0 - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - 0.00 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Priority: - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - medium - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - 10 - - - 14 - - - 6 - - - 4 - - - 6 - - - - - Fee: - - - 0 - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - 0.00 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Low Output: - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - no - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - 10 - - - 14 - - - 6 - - - 4 - - - 6 - - - - - After Fee: - - - 0 - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - 0.00 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Change - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - 0.00 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - + + 5 + + + + + + + Insufficient funds! + + + 5 + + + + + + + Reset + + + + + + + Consolidate Wizard + + + + + + + Qt::Horizontal + + + + 40 + 1 + + + - - - - - - 12 - - - QLayout::SetDefaultConstraint - - - 5 - - - 5 - - - - - custom change address + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + 0 - - - - - - false + + 0 - - - 0 - 0 - + + 0 - - Enter a Gridcoin address (e.g. S67nL4vELWwdDVzjgtEP4MxryarTZ9a8GB) + + 0 - - - - - - - - Qt::Vertical - - - - 800 - 1 - - - - - + + + + 20 + + + 0 + + + 10 + + + + + 10 + + + 14 + + + 10 + + + 4 + + + 6 + + + + + Quantity: + + + 0 + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Bytes: + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + Amount: + + + 0 + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Priority: + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + medium + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + Fee: + + + 0 + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Low Output: + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + no + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + 10 + + + 14 + + + 6 + + + 4 + + + 6 + + + + + After Fee: + + + 0 + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Change + + + + + + + IBeamCursor + + + Qt::ActionsContextMenu + + + 0.00 GRC + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + + + 12 + + + QLayout::SetDefaultConstraint + + + 5 + + + 5 + + + + + custom change address + + + + + + + false + + + + 0 + 0 + + + + Enter a Gridcoin address (e.g. S67nL4vELWwdDVzjgtEP4MxryarTZ9a8GB) + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + 3 + + + + + + + @@ -539,27 +688,30 @@ 0 0 - 879 - 212 + 897 + 223 + + 9 + - 0 + 12 - 0 + 12 - 0 + 12 - 0 + 12 - 6 + 9 @@ -581,127 +733,100 @@ - - - - - Send to multiple recipients at once - - - Add &Recipient - - - - :/icons/add:/icons/add - - - false - - - - - - - - 0 - 0 - - - - Remove all transaction fields - - - Clear &All - - - - :/icons/remove:/icons/remove - - - 300 - - - false - - - - - - - 3 - - - - - - 0 - 0 - - - - Balance: - - - - - - - - 0 - 0 - - - - IBeamCursor - - - 123.456 GRC - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 150 - 0 - - - - Confirm the send action - - - S&end - - - true - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Send to multiple recipients at once + + + Add &Recipient + + + + :/icons/add:/icons/add + + + false + + + + + + + + 0 + 0 + + + + Remove all transaction fields + + + Clear &All + + + + :/icons/remove:/icons/remove + + + 300 + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 150 + 0 + + + + Confirm the send action + + + S&end + + + true + + + + + - - - + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 1c6c763c0e..419557830d 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -594,6 +594,7 @@ QStatusBar QToolTip { #currentPollsFrame, #recentTransactionsFrame, #researcherFrame, +#SendCoinsEntry, #stakingFrame, #walletFrame { border-bottom: 0.065em solid rgba(0, 0, 0, 0.4); @@ -623,13 +624,15 @@ QStatusBar QToolTip { } #OverviewPage #headerBalanceLabel, -#OverviewPage #headerMagnitudeLabel { +#OverviewPage #headerMagnitudeLabel, +#SendCoinsDialog #headerBalanceLabel { color: rgb(26, 145, 235); font-weight: 500; } #OverviewPage #headerBalanceCaptionLabel, -#OverviewPage #headerMagnitudeCaptionLabel { +#OverviewPage #headerMagnitudeCaptionLabel, +#SendCoinsDialog #headerBalanceCaptionLabel { color: rgb(183, 189, 190); } @@ -677,12 +680,36 @@ QStatusBar QToolTip { border-radius: 0.26em; } -/* coincontrol dialog*/ +/* Send Coins Page */ -#coinControlFeaturesLabel{ - font-weight:bold; +#SendCoinsDialog #actionButtonsFrame { + background: rgba(0, 0, 0, 0.1); + border-top: 0.065em solid rgba(0, 0, 0, 0.3); + border-radius: 0; +} + +#SendCoinsEntry { + max-width: 60em; +} + +#coinControlWrapper { + background: rgba(0, 0, 0, 0.1); + border-bottom: 0.065em solid rgba(0, 0, 0, 0.3); + border-radius: 0; } +#coinControlFeaturesButton { + background: none; + border: none; + padding: 0; + color: rgb(204, 208, 209); + font-weight: bold; + text-align: left; + text-decoration: none; +} + +/* coincontrol dialog*/ + #coinControlInsuffFundsLabel{ color:red; font-weight:bold; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 66c38a7ba4..c52803cd6f 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -597,6 +597,7 @@ QStatusBar .QFrame QLabel { #currentPollsFrame, #recentTransactionsFrame, #researcherFrame, +#SendCoinsEntry, #stakingFrame, #walletFrame { border-bottom: 0.065em solid rgba(0, 0, 0, 0.1); @@ -626,7 +627,8 @@ QStatusBar .QFrame QLabel { } #OverviewPage #headerBalanceLabel, -#OverviewPage #headerMagnitudeLabel { +#OverviewPage #headerMagnitudeLabel, +#SendCoinsDialog #headerBalanceLabel { color: rgb(140, 20, 254); font-weight: 500; } @@ -675,12 +677,36 @@ QStatusBar .QFrame QLabel { border-radius: 0.26em; } -/* coincontrol dialog*/ +/* Send Coins Page */ -#coinControlFeaturesLabel{ - font-weight:bold; +#SendCoinsDialog #actionButtonsFrame { + background: rgb(244, 247, 249); + border-top: 0.065em solid rgba(0, 0, 0, 0.1); + border-radius: 0; +} + +#SendCoinsEntry { + max-width: 60em; +} + +#coinControlWrapper { + background: rgb(244, 247, 249); + border-bottom: 0.065em solid rgba(0, 0, 0, 0.1); + border-radius: 0; +} + +#coinControlFeaturesButton { + background: none; + border: none; + padding: 0; + color: rgb(58, 70, 93); + font-weight: bold; + text-align: left; + text-decoration: none; } +/* coincontrol dialog*/ + #coinControlInsuffFundsLabel{ color:red; font-weight:bold; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 10c296e4cf..bc33fce647 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -16,6 +16,7 @@ #include "coincontroldialog.h" #include "consolidateunspentdialog.h" #include "consolidateunspentwizard.h" +#include "qt/decoration.h" #include #include @@ -31,6 +32,11 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : model(0) { ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->headerTitleLabel, 15); + GRC::ScaleFontPointSize(ui->headerBalanceLabel, 14); + GRC::ScaleFontPointSize(ui->headerBalanceCaptionLabel, 8); + addEntry(); connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); @@ -97,7 +103,7 @@ void SendCoinsDialog::setModel(WalletModel *model) // Coin Control connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool))); - ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures()); + ui->coinControlContentWidget->setVisible(model->getOptionsModel()->getCoinControlFeatures()); coinControlUpdateLabels(); // set the icons according to the style options @@ -365,15 +371,20 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 stake, qint64 unconfirme return; int unit = model->getOptionsModel()->getDisplayUnit(); - ui->balanceLabel->setText(BitcoinUnits::formatWithUnit(unit, balance)); + + ui->headerBalanceLabel->setText(BitcoinUnits::format(unit, balance)); + ui->headerBalanceCaptionLabel->setText(tr("Available (%1)").arg(BitcoinUnits::name(unit))); } void SendCoinsDialog::updateDisplayUnit() { if(model && model->getOptionsModel()) { - // Update balanceLabel with the current balance and the current unit - ui->balanceLabel->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance())); + // Update headerBalanceLabel with the current balance and the current unit + int unit = model->getOptionsModel()->getDisplayUnit(); + + ui->headerBalanceLabel->setText(BitcoinUnits::format(unit, model->getBalance())); + ui->headerBalanceCaptionLabel->setText(tr("Available (%1)").arg(BitcoinUnits::name(unit))); } } @@ -428,7 +439,7 @@ void SendCoinsDialog::coinControlClipboardChange() // Coin Control: settings menu - coin control enabled/disabled by user void SendCoinsDialog::coinControlFeatureChanged(bool checked) { - ui->frameCoinControl->setVisible(checked); + ui->coinControlContentWidget->setVisible(checked); if (!checked && model) // coin control features disabled coinControl->SetNull(); From a5ae0f2507b8abd021fd6478c68c3355c86c3366 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Wed, 5 May 2021 21:12:14 -0500 Subject: [PATCH 107/280] Change send page coin control section to a toggleable panel This updates the coin control section on the send coins page to behave like a toggleable panel. Clicking on the header opens the coin control options, and clicking the label again hides them. When the panel is closed, coin control does not apply to the selection of inputs or the change address even when a user configured the fields prior to toggling the feature. To emphasize this, a status label shows when coin control settings influence a transaction. --- src/Makefile.qt.include | 2 + src/qt/bitcoin.qrc | 2 + src/qt/forms/sendcoinsdialog.ui | 2 +- src/qt/locale/bitcoin_af_ZA.ts | 2 +- src/qt/locale/bitcoin_ar.ts | 2 +- src/qt/locale/bitcoin_be_BY.ts | 2 +- src/qt/locale/bitcoin_bg.ts | 2 +- src/qt/locale/bitcoin_bs.ts | 2 +- src/qt/locale/bitcoin_ca.ts | 2 +- src/qt/locale/bitcoin_ca@valencia.ts | 2 +- src/qt/locale/bitcoin_ca_ES.ts | 2 +- src/qt/locale/bitcoin_cs.ts | 2 +- src/qt/locale/bitcoin_cy.ts | 2 +- src/qt/locale/bitcoin_da.ts | 2 +- src/qt/locale/bitcoin_de.ts | 2 +- src/qt/locale/bitcoin_el_GR.ts | 2 +- src/qt/locale/bitcoin_en.ts | 2 +- src/qt/locale/bitcoin_eo.ts | 2 +- src/qt/locale/bitcoin_es.ts | 2 +- src/qt/locale/bitcoin_es_CL.ts | 2 +- src/qt/locale/bitcoin_es_DO.ts | 2 +- src/qt/locale/bitcoin_es_MX.ts | 2 +- src/qt/locale/bitcoin_es_UY.ts | 2 +- src/qt/locale/bitcoin_et.ts | 2 +- src/qt/locale/bitcoin_eu_ES.ts | 2 +- src/qt/locale/bitcoin_fa.ts | 2 +- src/qt/locale/bitcoin_fa_IR.ts | 2 +- src/qt/locale/bitcoin_fi.ts | 2 +- src/qt/locale/bitcoin_fr.ts | 2 +- src/qt/locale/bitcoin_fr_CA.ts | 2 +- src/qt/locale/bitcoin_gl.ts | 2 +- src/qt/locale/bitcoin_he.ts | 2 +- src/qt/locale/bitcoin_hi_IN.ts | 2 +- src/qt/locale/bitcoin_hr.ts | 2 +- src/qt/locale/bitcoin_hu.ts | 2 +- src/qt/locale/bitcoin_id_ID.ts | 2 +- src/qt/locale/bitcoin_it.ts | 2 +- src/qt/locale/bitcoin_ja.ts | 2 +- src/qt/locale/bitcoin_ka.ts | 2 +- src/qt/locale/bitcoin_kk_KZ.ts | 2 +- src/qt/locale/bitcoin_ko_KR.ts | 2 +- src/qt/locale/bitcoin_ky.ts | 2 +- src/qt/locale/bitcoin_la.ts | 2 +- src/qt/locale/bitcoin_lt.ts | 2 +- src/qt/locale/bitcoin_lv_LV.ts | 2 +- src/qt/locale/bitcoin_ms_MY.ts | 2 +- src/qt/locale/bitcoin_nb.ts | 2 +- src/qt/locale/bitcoin_nl.ts | 2 +- src/qt/locale/bitcoin_pam.ts | 2 +- src/qt/locale/bitcoin_pl.ts | 2 +- src/qt/locale/bitcoin_pt_BR.ts | 2 +- src/qt/locale/bitcoin_pt_PT.ts | 2 +- src/qt/locale/bitcoin_ro_RO.ts | 2 +- src/qt/locale/bitcoin_ru.ts | 2 +- src/qt/locale/bitcoin_sk.ts | 2 +- src/qt/locale/bitcoin_sl_SI.ts | 2 +- src/qt/locale/bitcoin_sq.ts | 2 +- src/qt/locale/bitcoin_sr.ts | 2 +- src/qt/locale/bitcoin_sv.ts | 2 +- src/qt/locale/bitcoin_th_TH.ts | 2 +- src/qt/locale/bitcoin_tr.ts | 2 +- src/qt/locale/bitcoin_uk.ts | 2 +- src/qt/locale/bitcoin_ur_PK.ts | 2 +- src/qt/locale/bitcoin_vi.ts | 2 +- src/qt/locale/bitcoin_vi_VN.ts | 2 +- src/qt/locale/bitcoin_zh_CN.ts | 2 +- src/qt/locale/bitcoin_zh_TW.ts | 2 +- src/qt/optionsmodel.cpp | 5 ++ src/qt/optionsmodel.h | 1 + src/qt/res/icons/icons_dark/chevron_right.svg | 1 + .../res/icons/icons_light/chevron_right.svg | 1 + src/qt/res/stylesheets/dark_stylesheet.qss | 4 ++ src/qt/res/stylesheets/light_stylesheet.qss | 4 ++ src/qt/sendcoinsdialog.cpp | 54 +++++++++++++++++-- src/qt/sendcoinsdialog.h | 3 ++ 75 files changed, 137 insertions(+), 70 deletions(-) create mode 100644 src/qt/res/icons/icons_dark/chevron_right.svg create mode 100644 src/qt/res/icons/icons_light/chevron_right.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 091f8ca1d4..aea86071ca 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -448,8 +448,10 @@ RES_ICONS = \ qt/res/icons/icons_dark/transactions.svg \ qt/res/icons/icons_light/transactions.svg \ qt/res/icons/icons_light/chevron_down.svg \ + qt/res/icons/icons_light/chevron_right.svg \ qt/res/icons/icons_light/chevron_up.svg \ qt/res/icons/icons_dark/chevron_down.svg \ + qt/res/icons/icons_dark/chevron_right.svg \ qt/res/icons/icons_dark/chevron_up.svg RES_IMAGES = \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 610bf38e7d..d244a067fd 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -89,6 +89,7 @@ res/icons/icons_light/chevron_down.svg + res/icons/icons_light/chevron_right.svg res/icons/icons_light/chevron_up.svg res/icons/icons_light/settings.svg res/icons/icons_light/settings_action_needed.svg @@ -153,6 +154,7 @@ res/icons/icons_dark/chevron_down.svg + res/icons/icons_dark/chevron_right.svg res/icons/icons_dark/chevron_up.svg res/icons/icons_dark/settings_action_needed.svg diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 0a6cdecdc6..ae4b48f662 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -185,7 +185,7 @@ - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_af_ZA.ts b/src/qt/locale/bitcoin_af_ZA.ts index fe8abd2c0f..c00933b8c8 100644 --- a/src/qt/locale/bitcoin_af_ZA.ts +++ b/src/qt/locale/bitcoin_af_ZA.ts @@ -3204,7 +3204,7 @@ Dit beteken dat 'n fooi van ten minste %2 word benodig. - Coin Control Features + Coin Control Features (Advanced) Muntstuk beheer funksies diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts index a8c91646cf..9648dcb00e 100644 --- a/src/qt/locale/bitcoin_ar.ts +++ b/src/qt/locale/bitcoin_ar.ts @@ -3176,7 +3176,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_be_BY.ts b/src/qt/locale/bitcoin_be_BY.ts index ef613228b9..9bd68e60a2 100644 --- a/src/qt/locale/bitcoin_be_BY.ts +++ b/src/qt/locale/bitcoin_be_BY.ts @@ -3243,7 +3243,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts index b69a3f1b19..27a1f371b9 100644 --- a/src/qt/locale/bitcoin_bg.ts +++ b/src/qt/locale/bitcoin_bg.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Настройки за контрол на монетите diff --git a/src/qt/locale/bitcoin_bs.ts b/src/qt/locale/bitcoin_bs.ts index c04834f65b..fdca0f383e 100644 --- a/src/qt/locale/bitcoin_bs.ts +++ b/src/qt/locale/bitcoin_bs.ts @@ -3158,7 +3158,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts index d243edcafe..1bfa06ae81 100644 --- a/src/qt/locale/bitcoin_ca.ts +++ b/src/qt/locale/bitcoin_ca.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Característiques de control de les monedes diff --git a/src/qt/locale/bitcoin_ca@valencia.ts b/src/qt/locale/bitcoin_ca@valencia.ts index 527b7725ce..fcedcc9657 100644 --- a/src/qt/locale/bitcoin_ca@valencia.ts +++ b/src/qt/locale/bitcoin_ca@valencia.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Característiques de control de les monedes diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts index 490e5d62d1..896f8dc78c 100644 --- a/src/qt/locale/bitcoin_ca_ES.ts +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -3166,7 +3166,7 @@ En aquest cas es requereix una comisió d'almenys 2%. - Coin Control Features + Coin Control Features (Advanced) Característiques de control de les monedes diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index 8640f5dc1f..9b1d7416d7 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -3161,7 +3161,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Možnosti ruční správy mincí diff --git a/src/qt/locale/bitcoin_cy.ts b/src/qt/locale/bitcoin_cy.ts index 6781e200ac..ce84a08612 100644 --- a/src/qt/locale/bitcoin_cy.ts +++ b/src/qt/locale/bitcoin_cy.ts @@ -3190,7 +3190,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 8f0d467f51..b5596163ac 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -3169,7 +3169,7 @@ Det betyder, at et gebyr på mindst %2 er påkrævet. - Coin Control Features + Coin Control Features (Advanced) Egenskaber for coin-styring diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index dea2dddee2..bdcbf5609e 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -3171,7 +3171,7 @@ Dieses Label wird rot, wenn die Priorität kleiner ist als Mittel. - Coin Control Features + Coin Control Features (Advanced) "Coin Control"-Funktionen diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts index 7ac964749f..2e8cf17d58 100644 --- a/src/qt/locale/bitcoin_el_GR.ts +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -3153,7 +3153,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Χαρακτηρηστικά επιλογής κερμάτων diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 4892eedc46..cc69ea1861 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_eo.ts b/src/qt/locale/bitcoin_eo.ts index a2f8e0bed4..2138e45e44 100644 --- a/src/qt/locale/bitcoin_eo.ts +++ b/src/qt/locale/bitcoin_eo.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Monregaj Opcioj diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 65f199a579..610663bb9f 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -3170,7 +3170,7 @@ Esto significa que se requiere una cuota de al menos %2. - Coin Control Features + Coin Control Features (Advanced) Características de Coin Control diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts index c4515a9c79..030bbcdd14 100644 --- a/src/qt/locale/bitcoin_es_CL.ts +++ b/src/qt/locale/bitcoin_es_CL.ts @@ -3157,7 +3157,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_es_DO.ts b/src/qt/locale/bitcoin_es_DO.ts index 8787fae171..0702b3e2d4 100644 --- a/src/qt/locale/bitcoin_es_DO.ts +++ b/src/qt/locale/bitcoin_es_DO.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Características de control de la moneda diff --git a/src/qt/locale/bitcoin_es_MX.ts b/src/qt/locale/bitcoin_es_MX.ts index 035d6f2ee1..13b75c0479 100644 --- a/src/qt/locale/bitcoin_es_MX.ts +++ b/src/qt/locale/bitcoin_es_MX.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_es_UY.ts b/src/qt/locale/bitcoin_es_UY.ts index 784ec6b915..2cfab5c0c7 100644 --- a/src/qt/locale/bitcoin_es_UY.ts +++ b/src/qt/locale/bitcoin_es_UY.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts index a4a6eaee0e..0db8da6558 100644 --- a/src/qt/locale/bitcoin_et.ts +++ b/src/qt/locale/bitcoin_et.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_eu_ES.ts b/src/qt/locale/bitcoin_eu_ES.ts index 0645856ee9..77799e0e52 100644 --- a/src/qt/locale/bitcoin_eu_ES.ts +++ b/src/qt/locale/bitcoin_eu_ES.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts index 50747b2944..d52986a214 100644 --- a/src/qt/locale/bitcoin_fa.ts +++ b/src/qt/locale/bitcoin_fa.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts index fa684d8db2..938bf7b12f 100644 --- a/src/qt/locale/bitcoin_fa_IR.ts +++ b/src/qt/locale/bitcoin_fa_IR.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts index e05119d2c1..0786350cb2 100644 --- a/src/qt/locale/bitcoin_fi.ts +++ b/src/qt/locale/bitcoin_fi.ts @@ -3169,7 +3169,7 @@ Tämä tarkoittaa, että ainakin %2 rahansiirtopalkkio tarvitaan. - Coin Control Features + Coin Control Features (Advanced) Kolikkokontrolli ominaisuudet diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index bddaa53342..f9ce7def5c 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -3169,7 +3169,7 @@ Cela implique que des frais à hauteur d'au moins %2 seront nécessaires. - Coin Control Features + Coin Control Features (Advanced) Fonctions de contrôle des pièces diff --git a/src/qt/locale/bitcoin_fr_CA.ts b/src/qt/locale/bitcoin_fr_CA.ts index 11621933e0..71f2c1f0fe 100644 --- a/src/qt/locale/bitcoin_fr_CA.ts +++ b/src/qt/locale/bitcoin_fr_CA.ts @@ -3169,7 +3169,7 @@ Les montants inférieurs à 0.546 fois les frais minimum de relais apparaissent - Coin Control Features + Coin Control Features (Advanced) Fonctions de contrôle des monnaies diff --git a/src/qt/locale/bitcoin_gl.ts b/src/qt/locale/bitcoin_gl.ts index 8bd4630c15..8207dd7a96 100644 --- a/src/qt/locale/bitcoin_gl.ts +++ b/src/qt/locale/bitcoin_gl.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts index 54693595b1..5a79eed0a1 100644 --- a/src/qt/locale/bitcoin_he.ts +++ b/src/qt/locale/bitcoin_he.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) תכונות בקרת מטבעות diff --git a/src/qt/locale/bitcoin_hi_IN.ts b/src/qt/locale/bitcoin_hi_IN.ts index a599275c80..2249448329 100644 --- a/src/qt/locale/bitcoin_hi_IN.ts +++ b/src/qt/locale/bitcoin_hi_IN.ts @@ -3156,7 +3156,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_hr.ts b/src/qt/locale/bitcoin_hr.ts index aeb4e0b3b6..85d161b74e 100644 --- a/src/qt/locale/bitcoin_hr.ts +++ b/src/qt/locale/bitcoin_hr.ts @@ -3161,7 +3161,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts index ebd66d283d..f169ef731c 100644 --- a/src/qt/locale/bitcoin_hu.ts +++ b/src/qt/locale/bitcoin_hu.ts @@ -3149,7 +3149,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_id_ID.ts b/src/qt/locale/bitcoin_id_ID.ts index 31fd0c31e3..ba894a15be 100644 --- a/src/qt/locale/bitcoin_id_ID.ts +++ b/src/qt/locale/bitcoin_id_ID.ts @@ -3149,7 +3149,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Cara Pengaturan Koin diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index ca3af2c93a..a935366080 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -3169,7 +3169,7 @@ Questa etichetta diventa rossa se la priorità è minore di "media". - Coin Control Features + Coin Control Features (Advanced) Funzionalità di Coin Control diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 442c807bcc..1e984803b6 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) コインコントロール機能 diff --git a/src/qt/locale/bitcoin_ka.ts b/src/qt/locale/bitcoin_ka.ts index 8ef65526db..09a2f3ceae 100644 --- a/src/qt/locale/bitcoin_ka.ts +++ b/src/qt/locale/bitcoin_ka.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) მონეტების კონტროლის პარამეტრები diff --git a/src/qt/locale/bitcoin_kk_KZ.ts b/src/qt/locale/bitcoin_kk_KZ.ts index 75395cdc09..f91970881c 100644 --- a/src/qt/locale/bitcoin_kk_KZ.ts +++ b/src/qt/locale/bitcoin_kk_KZ.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index cfa08cc5b3..07d45f2a2f 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) 코인 컨트롤 기능들 diff --git a/src/qt/locale/bitcoin_ky.ts b/src/qt/locale/bitcoin_ky.ts index 9acc85ea3f..79727f93a1 100644 --- a/src/qt/locale/bitcoin_ky.ts +++ b/src/qt/locale/bitcoin_ky.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_la.ts b/src/qt/locale/bitcoin_la.ts index 97c809f7cb..35a617ca86 100644 --- a/src/qt/locale/bitcoin_la.ts +++ b/src/qt/locale/bitcoin_la.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts index 85143fbe6e..6e310734c7 100644 --- a/src/qt/locale/bitcoin_lt.ts +++ b/src/qt/locale/bitcoin_lt.ts @@ -3161,7 +3161,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_lv_LV.ts b/src/qt/locale/bitcoin_lv_LV.ts index 6aa0758e67..4b35e4b95a 100644 --- a/src/qt/locale/bitcoin_lv_LV.ts +++ b/src/qt/locale/bitcoin_lv_LV.ts @@ -3161,7 +3161,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Bitcoin Kontroles Funkcijas diff --git a/src/qt/locale/bitcoin_ms_MY.ts b/src/qt/locale/bitcoin_ms_MY.ts index e03468c0d1..4a3efe37e6 100644 --- a/src/qt/locale/bitcoin_ms_MY.ts +++ b/src/qt/locale/bitcoin_ms_MY.ts @@ -3147,7 +3147,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index ed5bf37639..7ae4755f10 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -3157,7 +3157,7 @@ Dette betyr at det trengs en avgift på minimum %2. - Coin Control Features + Coin Control Features (Advanced) Myntkontroll Funksjoner diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index 64a5c8d8b5..f50094d337 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -3169,7 +3169,7 @@ Dit betekend dat een fee van %2 is vereist. - Coin Control Features + Coin Control Features (Advanced) Coin controle opties diff --git a/src/qt/locale/bitcoin_pam.ts b/src/qt/locale/bitcoin_pam.ts index 809fc97968..f27e5581e8 100644 --- a/src/qt/locale/bitcoin_pam.ts +++ b/src/qt/locale/bitcoin_pam.ts @@ -2321,7 +2321,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index c7400009fe..663c7ded98 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -3179,7 +3179,7 @@ Wartości poniżej 0.546*minimalna wartość przekazu. są traktowane jako " - Coin Control Features + Coin Control Features (Advanced) Kontrola monet diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index 57cce3137e..db92c34fab 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -3155,7 +3155,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Opções de controle de moeda diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index 7c6563673e..ce569a3762 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -3180,7 +3180,7 @@ Isto significa que uma taxa de pelo menos %2 é necessária. - Coin Control Features + Coin Control Features (Advanced) Funcionalidades do Controlo de Moedas: diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts index 06304d15e2..682476a54a 100644 --- a/src/qt/locale/bitcoin_ro_RO.ts +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -3175,7 +3175,7 @@ Acest lucru înseamn? c? o tax? de cel pu?in %2 este necesar? - Coin Control Features + Coin Control Features (Advanced) Caracteristici de control ale monedei diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index 77823485be..a135ef7d87 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -3177,7 +3177,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Функции Контроля Монет diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index 0613131959..5d4ff9b563 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -3175,7 +3175,7 @@ To znamená, že je potrebný poplatok aspoň %2. - Coin Control Features + Coin Control Features (Advanced) Možnosti "Coin Control" diff --git a/src/qt/locale/bitcoin_sl_SI.ts b/src/qt/locale/bitcoin_sl_SI.ts index 86175b21ec..1900971145 100644 --- a/src/qt/locale/bitcoin_sl_SI.ts +++ b/src/qt/locale/bitcoin_sl_SI.ts @@ -3181,7 +3181,7 @@ Ta oznaka se obarva rde?e, ?e je prioriteta manjša kot "srednja". - Coin Control Features + Coin Control Features (Advanced) Upravljanje s kovanci diff --git a/src/qt/locale/bitcoin_sq.ts b/src/qt/locale/bitcoin_sq.ts index 69b1f48871..642206e640 100644 --- a/src/qt/locale/bitcoin_sq.ts +++ b/src/qt/locale/bitcoin_sq.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts index a864301208..ccdbd3fedf 100644 --- a/src/qt/locale/bitcoin_sr.ts +++ b/src/qt/locale/bitcoin_sr.ts @@ -3158,7 +3158,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index 9e2659bb71..52d538c977 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -3170,7 +3170,7 @@ Detta betyder att en avgift på minst %2 krävs. - Coin Control Features + Coin Control Features (Advanced) Myntkontrollfunktioner diff --git a/src/qt/locale/bitcoin_th_TH.ts b/src/qt/locale/bitcoin_th_TH.ts index dd6c3a414e..cef25c38de 100644 --- a/src/qt/locale/bitcoin_th_TH.ts +++ b/src/qt/locale/bitcoin_th_TH.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index df7bff22c2..3a9ae78a2a 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -3163,7 +3163,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Para kontrolü özellikleri diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index 2ee52e3060..ccdbd974b4 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -3158,7 +3158,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Керування монетами diff --git a/src/qt/locale/bitcoin_ur_PK.ts b/src/qt/locale/bitcoin_ur_PK.ts index 12f9733c5f..ab3088e506 100644 --- a/src/qt/locale/bitcoin_ur_PK.ts +++ b/src/qt/locale/bitcoin_ur_PK.ts @@ -3152,7 +3152,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_vi.ts b/src/qt/locale/bitcoin_vi.ts index e12155cb83..0b8d5123e2 100644 --- a/src/qt/locale/bitcoin_vi.ts +++ b/src/qt/locale/bitcoin_vi.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) diff --git a/src/qt/locale/bitcoin_vi_VN.ts b/src/qt/locale/bitcoin_vi_VN.ts index ab56e7a27c..7739f04490 100644 --- a/src/qt/locale/bitcoin_vi_VN.ts +++ b/src/qt/locale/bitcoin_vi_VN.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) Tính năng Control Coin diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 7e25761a05..3bbdab3643 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -3158,7 +3158,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) 货币支配特征 diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 686e79b544..9113a13713 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -3146,7 +3146,7 @@ This label turns red, if the priority is smaller than "medium". - Coin Control Features + Coin Control Features (Advanced) 錢幣控制功能 diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 3ceb0cd5b2..f160872b65 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -292,6 +292,11 @@ bool OptionsModel::getCoinControlFeatures() return fCoinControlFeatures; } +void OptionsModel::toggleCoinControlFeatures() +{ + setData(QAbstractItemModel::createIndex(CoinControlFeatures, 0), !fCoinControlFeatures, Qt::EditRole); +} + bool OptionsModel::getLimitTxnDisplay() { return fLimitTxnDisplay; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 580bfdc777..14ed35f5fb 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -67,6 +67,7 @@ class OptionsModel : public QAbstractListModel /* Explicit setters */ void setCurrentStyle(QString theme); + void toggleCoinControlFeatures(); private: int nDisplayUnit; diff --git a/src/qt/res/icons/icons_dark/chevron_right.svg b/src/qt/res/icons/icons_dark/chevron_right.svg new file mode 100644 index 0000000000..3f8262ddd9 --- /dev/null +++ b/src/qt/res/icons/icons_dark/chevron_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/chevron_right.svg b/src/qt/res/icons/icons_light/chevron_right.svg new file mode 100644 index 0000000000..3ef16cd292 --- /dev/null +++ b/src/qt/res/icons/icons_light/chevron_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 419557830d..20a43bffe2 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -708,6 +708,10 @@ QStatusBar QToolTip { text-decoration: none; } +#coinControlFeaturesButton:hover { + text-decoration: underline; +} + /* coincontrol dialog*/ #coinControlInsuffFundsLabel{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index c52803cd6f..05df259591 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -705,6 +705,10 @@ QStatusBar .QFrame QLabel { text-decoration: none; } +#coinControlFeaturesButton:hover { + text-decoration: underline; +} + /* coincontrol dialog*/ #coinControlInsuffFundsLabel{ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index bc33fce647..8dea3d4c61 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -44,6 +44,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : // Coin Control ui->coinControlChangeEdit->setFont(GUIUtil::bitcoinAddressFont()); + connect(ui->coinControlFeaturesButton, SIGNAL(clicked()), this, SLOT(toggleCoinControl())); connect(ui->coinControlPushButton, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); connect(ui->coinControlConsolidateWizardPushButton, SIGNAL(clicked()), this, SLOT(coinControlConsolidateWizardButtonClicked())); connect(ui->coinControlResetPushButton, SIGNAL(clicked()), this, SLOT(coinControlResetButtonClicked())); @@ -388,6 +389,13 @@ void SendCoinsDialog::updateDisplayUnit() } } +void SendCoinsDialog::toggleCoinControl() +{ + if (model && model->getOptionsModel()) { + model->getOptionsModel()->toggleCoinControlFeatures(); + } +} + // Coin Control: copy label "Quantity" to clipboard void SendCoinsDialog::coinControlClipboardQuantity() { @@ -440,9 +448,8 @@ void SendCoinsDialog::coinControlClipboardChange() void SendCoinsDialog::coinControlFeatureChanged(bool checked) { ui->coinControlContentWidget->setVisible(checked); - - if (!checked && model) // coin control features disabled - coinControl->SetNull(); + updateCoinControlIcon(); + coinControlUpdateLabels(); } // Coin Control: button inputs -> show actual coin control dialog @@ -515,6 +522,8 @@ void SendCoinsDialog::coinControlChangeChecked(int state) ui->coinControlChangeEdit->setEnabled((state == Qt::Checked)); ui->coinControlChangeLabel->setEnabled((state == Qt::Checked)); + + coinControlUpdateStatus(); } // Coin Control: custom change address changed @@ -525,9 +534,9 @@ void SendCoinsDialog::coinControlChangeEdited(const QString & text) coinControl->destChange = CBitcoinAddress(text.toStdString()).Get(); // label for the change address - ui->coinControlChangeLabel->setStyleSheet("QLabel{color:black;}"); + ui->coinControlChangeLabel->setStyleSheet(QString()); if (text.isEmpty()) - ui->coinControlChangeLabel->setText(""); + ui->coinControlChangeLabel->setText(QString()); else if (!CBitcoinAddress(text.toStdString()).IsValid()) { ui->coinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); @@ -558,6 +567,8 @@ void SendCoinsDialog::coinControlChangeEdited(const QString & text) // Coin Control: update labels void SendCoinsDialog::coinControlUpdateLabels() { + coinControlUpdateStatus(); + if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) return; @@ -587,6 +598,22 @@ void SendCoinsDialog::coinControlUpdateLabels() } } +void SendCoinsDialog::coinControlUpdateStatus() +{ + if (model + && model->getOptionsModel() + && model->getOptionsModel()->getCoinControlFeatures() + && (coinControl->HasSelected() || ui->coinControlChangeCheckBox->isChecked())) + { + ui->coinControlStatusLabel->setText(tr("Active")); + ui->coinControlStatusIconLabel->setPixmap(GRC::ScaleIcon(this, ":/icons/round_green_check", 16)); + return; + } + + ui->coinControlStatusLabel->setText(tr("Inactive")); + ui->coinControlStatusIconLabel->setPixmap(GRC::ScaleIcon(this, ":/icons/round_gray_x", 16)); +} + void SendCoinsDialog::updateIcons() { #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac @@ -599,4 +626,21 @@ void SendCoinsDialog::updateIcons() ui->sendButton->setIcon(QIcon(":/icons/send_"+model->getOptionsModel()->getCurrentStyle())); } #endif + + updateCoinControlIcon(); +} + +void SendCoinsDialog::updateCoinControlIcon() +{ + if (!model || !model->getOptionsModel()) { + return; + } + + const QString theme = model->getOptionsModel()->getCurrentStyle(); + + if (model->getOptionsModel()->getCoinControlFeatures()) { + ui->coinControlFeaturesButton->setIcon(QIcon(":/icons/" + theme + "_chevron_down")); + } else { + ui->coinControlFeaturesButton->setIcon(QIcon(":/icons/" + theme + "_chevron_right")); + } } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index b0479bd9ce..33903cc5f9 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -52,6 +52,7 @@ private slots: void on_sendButton_clicked(); void removeEntry(SendCoinsEntry* entry); void updateDisplayUnit(); + void toggleCoinControl(); void coinControlFeatureChanged(bool); void coinControlButtonClicked(); void coinControlResetButtonClicked(); @@ -59,6 +60,7 @@ private slots: void coinControlChangeChecked(int); void coinControlChangeEdited(const QString &); void coinControlUpdateLabels(); + void coinControlUpdateStatus(); void coinControlClipboardQuantity(); void coinControlClipboardAmount(); void coinControlClipboardFee(); @@ -69,6 +71,7 @@ private slots: void coinControlClipboardChange(); void selectedConsolidationRecipient(SendCoinsRecipient consolidationRecipient); void updateIcons(); + void updateCoinControlIcon(); }; #endif // SENDCOINSDIALOG_H From 18cf27cb203246c0aaf5093e6bed2b0d6c8bb8db Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Wed, 5 May 2021 21:13:03 -0500 Subject: [PATCH 108/280] Remove coin control checkbox from options dialog Since the coin control settings are always available now on the "send coins" page, this removes the coin control features checkbox from the options dialog. The options model still controls the expansion states of the coin control panel to remember the state a user selected last. --- src/qt/forms/optionsdialog.ui | 10 ---------- src/qt/optionsdialog.cpp | 1 - 2 files changed, 11 deletions(-) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 1fdaf00bd6..2f24fb4e88 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -382,16 +382,6 @@ - - - - Whether to show coin control features or not. - - - Display coin &control features (advanced users only!) - - - diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index c2b582065e..45749d48ba 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -157,7 +157,6 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->lang, OptionsModel::Language); mapper->addMapping(ui->unit, OptionsModel::DisplayUnit); mapper->addMapping(ui->styleComboBox, OptionsModel::WalletStylesheet,"currentData"); - mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); mapper->addMapping(ui->limitTxnDisplayCheckBox, OptionsModel::LimitTxnDisplay); mapper->addMapping(ui->limitTxnDisplayDateEdit, OptionsModel::LimitTxnDate); mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses); From bfc6f348afb389d17c741bce0fce1a2e7f34a823 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 16 May 2021 10:51:10 -0500 Subject: [PATCH 109/280] Fix coin control change address status label The status label for the "custom change address" field in the coin control section of the "send payment" page was removed by accident because it overlapped with the name of the change amount label. This fixes the change address field's status label by changing the object name so that the page doesn't overwrite the status text for updates that target the change amount label. --- src/qt/forms/sendcoinsdialog.ui | 14 ++++++++------ src/qt/sendcoinsdialog.cpp | 18 +++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index ae4b48f662..ef5b881d3d 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -192,7 +192,7 @@ - :/icons/round_gray_x + :/icons/round_gray_x @@ -589,7 +589,7 @@ - + IBeamCursor @@ -649,7 +649,7 @@ - + 0 @@ -756,7 +756,7 @@ Add &Recipient - + :/icons/add:/icons/add @@ -779,7 +779,7 @@ Clear &All - + :/icons/remove:/icons/remove @@ -827,6 +827,8 @@ - + + + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 8dea3d4c61..2f7d7bed0b 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -521,7 +521,7 @@ void SendCoinsDialog::coinControlChangeChecked(int state) } ui->coinControlChangeEdit->setEnabled((state == Qt::Checked)); - ui->coinControlChangeLabel->setEnabled((state == Qt::Checked)); + ui->coinControlChangeAddressLabel->setEnabled((state == Qt::Checked)); coinControlUpdateStatus(); } @@ -534,30 +534,30 @@ void SendCoinsDialog::coinControlChangeEdited(const QString & text) coinControl->destChange = CBitcoinAddress(text.toStdString()).Get(); // label for the change address - ui->coinControlChangeLabel->setStyleSheet(QString()); + ui->coinControlChangeAddressLabel->setStyleSheet(QString()); if (text.isEmpty()) - ui->coinControlChangeLabel->setText(QString()); + ui->coinControlChangeAddressLabel->setText(QString()); else if (!CBitcoinAddress(text.toStdString()).IsValid()) { - ui->coinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); - ui->coinControlChangeLabel->setText(tr("WARNING: Invalid Gridcoin address")); + ui->coinControlChangeAddressLabel->setStyleSheet("QLabel{color:red;}"); + ui->coinControlChangeAddressLabel->setText(tr("WARNING: Invalid Gridcoin address")); } else { QString associatedLabel = model->getAddressTableModel()->labelForAddress(text); if (!associatedLabel.isEmpty()) - ui->coinControlChangeLabel->setText(associatedLabel); + ui->coinControlChangeAddressLabel->setText(associatedLabel); else { CPubKey pubkey; CKeyID keyid; CBitcoinAddress(text.toStdString()).GetKeyID(keyid); if (model->getPubKey(keyid, pubkey)) - ui->coinControlChangeLabel->setText(tr("(no label)")); + ui->coinControlChangeAddressLabel->setText(tr("(no label)")); else { - ui->coinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); - ui->coinControlChangeLabel->setText(tr("WARNING: unknown change address")); + ui->coinControlChangeAddressLabel->setStyleSheet("QLabel{color:red;}"); + ui->coinControlChangeAddressLabel->setText(tr("WARNING: unknown change address")); } } } From ee2fbfb69c122ff7216118b7d8f99444ef25ed07 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 17 May 2021 12:45:41 -0500 Subject: [PATCH 110/280] Skip GUI locking cs_main for periodic updates This change skips update intervals when the GUI cannot obtain a lock for "cs_main". It avoids stalling the GUI thread when a long-running operation holds the mutex for: - The overview page refresh - The researcher context refresh --- src/qt/bitcoingui.cpp | 6 ++++++ src/qt/researcher/researchermodel.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a3f984d69c..d44f1b6ef1 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1513,6 +1513,12 @@ void BitcoinGUI::updateGlobalStatus() // This is needed to prevent segfaulting due to early GUI initialization compared to core. if (miner_first_pass_complete) { + TRY_LOCK(cs_main, locked_main); + + if (!locked_main) { + return; + } + try { overviewPage->updateGlobalStatus(); diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 481411ea93..06283b38ac 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -491,6 +491,12 @@ void ResearcherModel::refresh() emit researcherChanged(); } + TRY_LOCK(cs_main, lockMain); + + if (!lockMain) { + return; + } + updateBeacon(); emit magnitudeChanged(); From 7ed188d6419fcef971142ce8ece156d816217b3f Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 17 May 2021 19:36:38 -0500 Subject: [PATCH 111/280] Optimize GUI locks for tx generated type lookups This optimizes how the GUI determines the "mined" type of a coinstake transaction. It reorganizes the lookups to avoid the cs_main locks if the node is busy with another operation. This prevents GUI stalls for routine updates of the overview and history pages from core state. It prevents the long-running polls load on the voting page from bricking the GUI thread. --- src/Makefile.am | 1 + src/qt/transactionrecord.cpp | 2 ++ src/qt/transactionrecord.h | 14 +++++++++++--- src/qt/transactiontablemodel.cpp | 8 ++------ src/wallet/generated_type.h | 19 +++++++++++++++++++ src/wallet/wallet.h | 23 ++++++++--------------- 6 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 src/wallet/generated_type.h diff --git a/src/Makefile.am b/src/Makefile.am index 90a9e9fcee..2edac1556e 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,6 +178,7 @@ GRIDCOIN_CORE_H = \ version.h \ wallet/coincontrol.h \ wallet/db.h \ + wallet/generated_type.h \ wallet/walletdb.h \ wallet/wallet.h \ wallet/ismine.h diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 9e7071fff0..e2994058f0 100755 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -406,6 +406,8 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) { status.status = TransactionStatus::Confirmed; } + + status.generated_type = wtx.GetGeneratedType(vout); } else { diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index a554e6edc7..0b66b54b28 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -2,6 +2,7 @@ #define TRANSACTIONRECORD_H #include "uint256.h" +#include "wallet/generated_type.h" #include "wallet/ismine.h" #include @@ -14,9 +15,15 @@ class CWalletTx; class TransactionStatus { public: - TransactionStatus(): - countsForBalance(false), sortKey(""), - matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) + TransactionStatus() + : countsForBalance(false) + , sortKey("") + , matures_in(0) + , status(Offline) + , generated_type(MinedType::UNKNOWN) + , depth(0) + , open_for(0) + , cur_num_blocks(-1) { } enum Status { @@ -47,6 +54,7 @@ class TransactionStatus /** @name Reported status @{*/ Status status; + MinedType generated_type; int64_t depth; int64_t open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined before diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 3692ae5a2c..e5514651e7 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -401,9 +401,7 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return tr("Payment to yourself"); case TransactionRecord::Generated: { - MinedType gentype = GetGeneratedType(wallet, wtx->hash, wtx->vout); - - switch (gentype) + switch (wtx->status.generated_type) { case MinedType::POS: return tr("MINED - POS"); @@ -445,9 +443,7 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx { case TransactionRecord::Generated: { - MinedType gentype = GetGeneratedType(wallet, wtx->hash, wtx->vout); - - switch (gentype) + switch (wtx->status.generated_type) { case MinedType::POS: return QIcon(":/icons/tx_pos"); diff --git a/src/wallet/generated_type.h b/src/wallet/generated_type.h new file mode 100644 index 0000000000..9303faa252 --- /dev/null +++ b/src/wallet/generated_type.h @@ -0,0 +1,19 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +/** (POS/POR) enums for CoinStake Transactions -- We should never get unknown but just in case!*/ +enum MinedType +{ + UNKNOWN = 0, + POS = 1, + POR = 2, + ORPHANED = 3, + POS_SIDE_STAKE_RCV = 4, + POR_SIDE_STAKE_RCV = 5, + POS_SIDE_STAKE_SEND = 6, + POR_SIDE_STAKE_SEND = 7, + SUPERBLOCK = 8 +}; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9c965c3f70..520e0225e1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -19,6 +19,7 @@ #include "script.h" #include "streams.h" #include "ui_interface.h" +#include "wallet/generated_type.h" #include "wallet/walletdb.h" #include "wallet/ismine.h" @@ -30,6 +31,8 @@ class CReserveKey; class COutput; class CCoinControl; +MinedType GetGeneratedType(const CWallet *wallet, const uint256& tx, unsigned int vout); + /** (client) version numbers for particular wallet features */ enum WalletFeature { @@ -39,20 +42,6 @@ enum WalletFeature FEATURE_LATEST = 60000 }; -/** (POS/POR) enums for CoinStake Transactions -- We should never get unknown but just in case!*/ -enum MinedType -{ - UNKNOWN = 0, - POS = 1, - POR = 2, - ORPHANED = 3, - POS_SIDE_STAKE_RCV = 4, - POR_SIDE_STAKE_RCV = 5, - POS_SIDE_STAKE_SEND = 6, - POR_SIDE_STAKE_SEND = 7, - SUPERBLOCK = 8 -}; - /** A key pool entry */ class CKeyPool { @@ -854,6 +843,11 @@ class CWalletTx : public CMerkleTx void RelayWalletTransaction(CTxDB& txdb); void RelayWalletTransaction(); + + MinedType GetGeneratedType(uint32_t vout_offset) const + { + return ::GetGeneratedType(pwallet, GetHash(), vout_offset); + } }; @@ -1048,5 +1042,4 @@ class CAccountingEntry std::vector _ssExtra; }; -MinedType GetGeneratedType(const CWallet *wallet, const uint256& tx, unsigned int vout); #endif From 263f5c3f4ef0f7d5e601b4df6775a32f1a9cb866 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 11 May 2021 23:07:00 -0500 Subject: [PATCH 112/280] Fix "verify message" button visible on receive page The "verify message" button initially appears on the "receive payment" page in a disabled state and disappears when selecting an entry in the address table. Since the button is never accessible, this hides it for the "receive payment" page as a whole. --- src/qt/addressbookpage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 7189788b12..ed1b196241 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -57,6 +57,7 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : break; case ReceivingTab: ui->deleteButton->setVisible(false); + ui->verifyMessageButton->setVisible(false); ui->signMessageButton->setVisible(true); break; } From 1e584467b9389a6c3cef547b8c3d1281d545e83d Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 11 May 2021 23:07:03 -0500 Subject: [PATCH 113/280] Add API to filter address book model by address/label This adds the ability to filter address book tables by a string that contains a partial address or label. The filter is case-insensitive. --- src/qt/addressbookpage.cpp | 15 ++++++++++++++- src/qt/addressbookpage.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index ed1b196241..67012e381f 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -116,6 +116,7 @@ void AddressBookPage::setModel(AddressTableModel *model) proxyModel->setDynamicSortFilter(true); proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + switch(tab) { case ReceivingTab: @@ -129,7 +130,14 @@ void AddressBookPage::setModel(AddressTableModel *model) proxyModel->setFilterFixedString(AddressTableModel::Send); break; } - ui->tableView->setModel(proxyModel); + + filterProxyModel = new QSortFilterProxyModel(this); + filterProxyModel->setSourceModel(proxyModel); + filterProxyModel->setDynamicSortFilter(true); + filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + filterProxyModel->setFilterKeyColumn(-1); // All columns + + ui->tableView->setModel(filterProxyModel); ui->tableView->sortByColumn(0, Qt::AscendingOrder); // Set column widths @@ -333,6 +341,11 @@ void AddressBookPage::exportClicked() } } +void AddressBookPage::changeFilter(const QString& needle) +{ + filterProxyModel->setFilterFixedString(needle); +} + void AddressBookPage::on_showQRCodeButton_clicked() { #ifdef USE_QRCODE diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index ffd92f9b9a..64c900542e 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -44,6 +44,7 @@ class AddressBookPage : public QDialog public slots: void done(int retval); void exportClicked(); + void changeFilter(const QString& needle); private: Ui::AddressBookPage *ui; @@ -53,6 +54,7 @@ public slots: Tabs tab; QString returnValue; QSortFilterProxyModel *proxyModel; + QSortFilterProxyModel *filterProxyModel; QMenu *contextMenu; QAction *deleteAction; QString newAddressToSelect; From 61209748d5c8bf71d7f87ec0d8e20e4b1150c80f Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 11 May 2021 23:07:06 -0500 Subject: [PATCH 114/280] Refresh GUI receive payment page design This updates the GUI "receive payment" page to match the proposed redesign: - Add a header bar with address/label filter input - Limit the max content width for large window sizes - Update colors, layout, sizing, and spacing --- gridcoinresearch.pro | 3 + src/Makefile.qt.include | 6 + src/qt/bitcoin.qrc | 2 + src/qt/bitcoingui.cpp | 13 +- src/qt/bitcoingui.h | 4 +- src/qt/forms/addressbookpage.ui | 356 ++++++++++++-------- src/qt/forms/receivecoinspage.ui | 180 ++++++++++ src/qt/receivecoinspage.cpp | 64 ++++ src/qt/receivecoinspage.h | 51 +++ src/qt/res/icons/icons_dark/search.svg | 1 + src/qt/res/icons/icons_light/search.svg | 1 + src/qt/res/stylesheets/dark_stylesheet.qss | 25 +- src/qt/res/stylesheets/light_stylesheet.qss | 25 +- 13 files changed, 569 insertions(+), 162 deletions(-) create mode 100644 src/qt/forms/receivecoinspage.ui create mode 100644 src/qt/receivecoinspage.cpp create mode 100644 src/qt/receivecoinspage.h create mode 100644 src/qt/res/icons/icons_dark/search.svg create mode 100644 src/qt/res/icons/icons_light/search.svg diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index d55052147b..79f85ade05 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -190,6 +190,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/optionsdialog.h \ src/qt/coincontroldialog.h \ src/qt/coincontroltreewidget.h \ + src/qt/receivecoinspage.h \ src/qt/sendcoinsdialog.h \ src/qt/addressbookpage.h \ src/qt/signverifymessagedialog.h \ @@ -292,6 +293,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ src/qt/optionsdialog.cpp \ + src/qt/receivecoinspage.cpp \ src/qt/sendcoinsdialog.cpp \ src/qt/coincontroldialog.cpp \ src/qt/coincontroltreewidget.cpp \ @@ -388,6 +390,7 @@ FORMS += \ src/qt/forms/researcherwizardpoolsummarypage.ui \ src/qt/forms/researcherwizardprojectspage.ui \ src/qt/forms/researcherwizardsummarypage.ui \ + src/qt/forms/receivecoinspage.ui \ src/qt/forms/sendcoinsdialog.ui \ src/qt/forms/addressbookpage.ui \ src/qt/forms/signverifymessagedialog.ui \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index aea86071ca..46b5b19af0 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -86,6 +86,7 @@ QT_FORMS_UI = \ qt/forms/addressbookpage.ui \ qt/forms/editaddressdialog.ui \ qt/forms/overviewpage.ui \ + qt/forms/receivecoinspage.ui \ qt/forms/researcherwizard.ui \ qt/forms/researcherwizardauthpage.ui \ qt/forms/researcherwizardbeaconpage.ui \ @@ -136,6 +137,7 @@ QT_MOC_CPP = \ qt/moc_peertablemodel.cpp \ qt/moc_qvalidatedlineedit.cpp \ qt/moc_qvaluecombobox.cpp \ + qt/moc_receivecoinspage.cpp \ qt/moc_rpcconsole.cpp \ qt/moc_sendcoinsdialog.cpp \ qt/moc_sendcoinsentry.cpp \ @@ -215,6 +217,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/qtipcserver.h \ qt/qvalidatedlineedit.h \ qt/qvaluecombobox.h \ + qt/receivecoinspage.h \ qt/researcher/projecttablemodel.h \ qt/researcher/researchermodel.h \ qt/researcher/researcherwizard.h \ @@ -278,6 +281,7 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/qtipcserver.cpp \ qt/qvalidatedlineedit.cpp \ qt/qvaluecombobox.cpp \ + qt/receivecoinspage.cpp \ qt/researcher/projecttablemodel.cpp \ qt/researcher/researchermodel.cpp \ qt/researcher/researcherwizard.cpp \ @@ -362,6 +366,7 @@ RES_ICONS = \ qt/res/icons/warning.svg \ qt/res/icons/white_and_red_x.svg \ qt/res/icons/www.png \ + qt/res/icons/icons_light/search.svg \ qt/res/icons/icons_light/settings.svg \ qt/res/icons/icons_light/settings_action_needed.svg \ qt/res/icons/icons_light/sidebar_favorites_active.svg \ @@ -404,6 +409,7 @@ RES_ICONS = \ qt/res/icons/icons_light/status_sync_done.svg \ qt/res/icons/icons_light/status_sync_stalled.svg \ qt/res/icons/icons_light/status_sync_syncing.svg \ + qt/res/icons/icons_dark/search.svg \ qt/res/icons/icons_dark/settings_action_needed.svg \ qt/res/icons/icons_dark/sidebar_favorites_active.svg \ qt/res/icons/icons_dark/sidebar_favorites_inactive.svg \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index d244a067fd..ed296ff2dc 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -91,6 +91,7 @@ res/icons/icons_light/chevron_down.svg res/icons/icons_light/chevron_right.svg res/icons/icons_light/chevron_up.svg + res/icons/icons_light/search.svg res/icons/icons_light/settings.svg res/icons/icons_light/settings_action_needed.svg @@ -156,6 +157,7 @@ res/icons/icons_dark/chevron_down.svg res/icons/icons_dark/chevron_right.svg res/icons/icons_dark/chevron_up.svg + res/icons/icons_dark/search.svg res/icons/icons_dark/settings_action_needed.svg res/icons/tx_pos_ss.svg diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a3f984d69c..aa0de40014 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -15,6 +15,7 @@ #include "addressbookpage.h" #include "diagnosticsdialog.h" +#include "receivecoinspage.h" #include "sendcoinsdialog.h" #include "signverifymessagedialog.h" #include "optionsdialog.h" @@ -194,11 +195,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): addressBookPageLayout->addWidget(addressBook); addressBookPage->setLayout(addressBookPageLayout); - receiveCoinsPage = new QWidget(this); - receiveAddressBook = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); - QVBoxLayout *receiveCoinsPageLayout = new QVBoxLayout(); - receiveCoinsPageLayout->addWidget(receiveAddressBook); - receiveCoinsPage->setLayout(receiveCoinsPageLayout); + receiveCoinsPage = new ReceiveCoinsPage(this); sendCoinsPage = new SendCoinsDialog(this); @@ -765,7 +762,7 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) rpcConsole->setClientModel(clientModel); addressBook->setOptionsModel(clientModel->getOptionsModel()); - receiveAddressBook->setOptionsModel(clientModel->getOptionsModel()); + receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); } } @@ -786,7 +783,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) overviewPage->setWalletModel(walletModel); addressBook->setModel(walletModel->getAddressTableModel()); - receiveAddressBook->setModel(walletModel->getAddressTableModel()); + receiveCoinsPage->setAddressTableModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); votingPage->setModel(walletModel); signVerifyMessageDialog->setModel(walletModel); @@ -1304,7 +1301,7 @@ void BitcoinGUI::gotoReceiveCoinsPage() exportAction->setEnabled(true); disconnect(exportAction, SIGNAL(triggered()), 0, 0); - connect(exportAction, SIGNAL(triggered()), receiveAddressBook, SLOT(exportClicked())); + connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked())); } void BitcoinGUI::gotoSendCoinsPage() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 181e5b7c29..6913a6f4d8 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -19,6 +19,7 @@ class ResearcherModel; class TransactionView; class OverviewPage; class AddressBookPage; +class ReceiveCoinsPage; class SendCoinsDialog; class VotingDialog; class SignVerifyMessageDialog; @@ -80,7 +81,7 @@ class BitcoinGUI : public QMainWindow OverviewPage *overviewPage; QWidget *transactionsPage; QWidget *addressBookPage; - QWidget *receiveCoinsPage; + ReceiveCoinsPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; VotingDialog *votingPage; SignVerifyMessageDialog *signVerifyMessageDialog; @@ -136,7 +137,6 @@ class BitcoinGUI : public QMainWindow Notificator *notificator; TransactionView *transactionView; AddressBookPage *addressBook; - AddressBookPage *receiveAddressBook; RPCConsole *rpcConsole; DiagnosticsDialog *diagnosticsDialog; diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index 07c016ab55..41a9250987 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -14,165 +14,225 @@ Address Book + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - - - These are your Gridcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + QFrame::StyledPanel - - Qt::PlainText - - - true + + QFrame::Raised + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + These are your Gridcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + Qt::PlainText + + + true + + + + + + + Qt::CustomContextMenu + + + Double-click to edit address or label + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + true + + + false + + + false + + + + - - - Qt::CustomContextMenu - - - Double-click to edit address or label - - - false - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - + + true - - false - + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Create a new address + + + &New + + + + :/icons/add:/icons/add + + + + + + + Copy the currently selected address to the system clipboard + + + &Copy + + + + :/icons/editcopy:/icons/editcopy + + + + + + + Show &QR Code + + + + :/icons/qrcode:/icons/qrcode + + + + + + + Sign a message to prove you own a Gridcoin address + + + Sign &Message + + + + :/icons/edit:/icons/edit + + + + + + + Verify a message to ensure it was signed with a specified Gridcoin address + + + &Verify Message + + + + :/icons/transaction_0:/icons/transaction_0 + + + + + + + Delete the currently selected address from the list + + + &Delete + + + + :/icons/remove:/icons/remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Ok + + + + - - - - - - Create a new address - - - &New - - - - :/icons/add:/icons/add - - - - - - - Copy the currently selected address to the system clipboard - - - &Copy - - - - :/icons/editcopy:/icons/editcopy - - - - - - - Show &QR Code - - - - :/icons/qrcode:/icons/qrcode - - - - - - - Sign a message to prove you own a Gridcoin address - - - Sign &Message - - - - :/icons/edit:/icons/edit - - - - - - - Verify a message to ensure it was signed with a specified Gridcoin address - - - &Verify Message - - - - :/icons/transaction_0:/icons/transaction_0 - - - - - - - Delete the currently selected address from the list - - - &Delete - - - - :/icons/remove:/icons/remove - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - QDialogButtonBox::Ok - - - - - - - - + diff --git a/src/qt/forms/receivecoinspage.ui b/src/qt/forms/receivecoinspage.ui new file mode 100644 index 0000000000..9c6fecf8d9 --- /dev/null +++ b/src/qt/forms/receivecoinspage.ui @@ -0,0 +1,180 @@ + + + ReceiveCoinsPage + + + + 0 + 0 + 899 + 456 + + + + Receive Payment + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Receive Payment + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Search by label or address + + + true + + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + 1 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 9 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + diff --git a/src/qt/receivecoinspage.cpp b/src/qt/receivecoinspage.cpp new file mode 100644 index 0000000000..17df69b0f2 --- /dev/null +++ b/src/qt/receivecoinspage.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/addressbookpage.h" +#include "qt/decoration.h" +#include "qt/forms/ui_receivecoinspage.h" +#include "qt/optionsmodel.h" +#include "qt/receivecoinspage.h" + +#include +#include + +ReceiveCoinsPage::ReceiveCoinsPage(QWidget *parent) + : QWidget(parent) + , ui(new Ui::ReceiveCoinsPage) + , addressBookPage(new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab)) + , filterLineEditIconAction(new QAction()) +{ + ui->setupUi(this); + ui->contentFrameVerticalLayout->addWidget(addressBookPage); + ui->filterLineEdit->addAction(filterLineEditIconAction, QLineEdit::LeadingPosition); + + GRC::ScaleFontPointSize(ui->headerTitleLabel, 15); + + connect( + ui->filterLineEdit, &QLineEdit::textChanged, + addressBookPage, &AddressBookPage::changeFilter); + connect( + addressBookPage, &AddressBookPage::signMessage, + [this](const QString& address) { emit signMessage(address); }); +} + +ReceiveCoinsPage::~ReceiveCoinsPage() +{ + delete ui; + delete addressBookPage; + delete filterLineEditIconAction; +} + +void ReceiveCoinsPage::setAddressTableModel(AddressTableModel *model) +{ + addressBookPage->setModel(model); +} + +void ReceiveCoinsPage::setOptionsModel(OptionsModel *model) +{ + addressBookPage->setOptionsModel(model); + + if (model) { + connect(model, SIGNAL(walletStylesheetChanged(QString)), this, SLOT(updateIcons(QString))); + updateIcons(model->getCurrentStyle()); + } +} + +void ReceiveCoinsPage::exportClicked() +{ + addressBookPage->exportClicked(); +} + +void ReceiveCoinsPage::updateIcons(const QString& theme) +{ + filterLineEditIconAction->setIcon(QIcon(":/icons/" + theme + "_search")); +} diff --git a/src/qt/receivecoinspage.h b/src/qt/receivecoinspage.h new file mode 100644 index 0000000000..40b2a36639 --- /dev/null +++ b/src/qt/receivecoinspage.h @@ -0,0 +1,51 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef RECEIVECOINSPAGE_H +#define RECEIVECOINSPAGE_H + +#include "walletmodel.h" + +#include + +namespace Ui { + class ReceiveCoinsPage; +} + +class AddressBookPage; +class AddressTableModel; +class OptionsModel; + +QT_BEGIN_NAMESPACE +class QAction; +class QString; +QT_END_NAMESPACE + +class ReceiveCoinsPage : public QWidget +{ + Q_OBJECT + +public: + explicit ReceiveCoinsPage(QWidget *parent = nullptr); + ~ReceiveCoinsPage(); + + void setAddressTableModel(AddressTableModel *model); + void setOptionsModel(OptionsModel *model); + +private: + Ui::ReceiveCoinsPage *ui; + AddressBookPage *addressBookPage; + QAction *filterLineEditIconAction; + +signals: + void signMessage(QString address); + +public slots: + void exportClicked(); + +private slots: + void updateIcons(const QString& theme); +}; + +#endif // RECEIVECOINSPAGE_H diff --git a/src/qt/res/icons/icons_dark/search.svg b/src/qt/res/icons/icons_dark/search.svg new file mode 100644 index 0000000000..a81cecde97 --- /dev/null +++ b/src/qt/res/icons/icons_dark/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/search.svg b/src/qt/res/icons/icons_light/search.svg new file mode 100644 index 0000000000..834a286af7 --- /dev/null +++ b/src/qt/res/icons/icons_light/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 20a43bffe2..cd102fe68a 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -35,7 +35,6 @@ QTreeWidget { } .QFrame, -AddressBookPage, QGroupBox, QTabWidget::pane, #SendCoinsEntry, @@ -46,6 +45,20 @@ TransactionView { padding: 0.75em 1em; } +AddressBookPage #wrapperFrame { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +QFrame[buttonFrame=true] { + background: rgb(22, 27, 36); + border-top: 0.065em solid rgba(0, 0, 0, 0.25); + border-bottom: 0.065em solid rgba(0, 0, 0, 0.4); + border-top-left-radius: 0; + border-top-right-radius: 0; + padding: 0.5em 1em; +} + /* HLine */ QFrame[frameShape="4"] { border-top: 0.065em solid rgba(255, 255, 255, 0.1); @@ -289,7 +302,8 @@ QSpinBox::down-button:hover, QTextEdit:focus, QToolButton:hover, QToolButton:focus, -QToolButton:selected { +QToolButton:selected, +#headerFrame QLineEdit:focus { border-color: rgb(21, 126, 205); color: rgb(195, 199, 201); } @@ -584,6 +598,13 @@ QStatusBar QToolTip { font-weight: 500; } +#headerFrame QLineEdit { + background: rgba(23, 27, 36, 0.7); + border-color: rgba(0, 0, 0, 0.3); + max-width: 20em; + padding: 0.35em; +} + #contentFrame { background: none; max-width: 60em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 05df259591..5e32010ac3 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -31,7 +31,6 @@ QTreeWidget { } .QFrame, -AddressBookPage, QGroupBox, QTabWidget::pane, #SendCoinsEntry, @@ -42,6 +41,20 @@ TransactionView { padding: 0.75em 1em; } +AddressBookPage #wrapperFrame { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +QFrame[buttonFrame=true] { + background: rgb(244, 247, 249); + border-top: 0.065em solid rgba(0, 0, 0, 0.03); + border-bottom: 0.065em solid rgba(0, 0, 0, 0.1); + border-top-left-radius: 0; + border-top-right-radius: 0; + padding: 0.5em 1em; +} + /* HLine */ QFrame[frameShape="4"] { border-top: 0.065em solid rgb(234, 237, 237); @@ -292,7 +305,8 @@ QSpinBox::down-button:hover, QTextEdit:focus, QToolButton:hover, QToolButton:focus, -QToolButton:selected { +QToolButton:selected, +#headerFrame QLineEdit:focus { border-color: rgb(120, 20, 255); color: rgb(88, 92, 107); } @@ -587,6 +601,13 @@ QStatusBar .QFrame QLabel { font-weight: 500; } +#headerFrame QLineEdit { + background: rgb(244, 247, 249); + border-color: rgba(58, 70, 93, 0.3); + max-width: 20em; + padding: 0.35em; +} + #contentFrame { background: none; max-width: 60em; From bb36e29da855ecd3f0bbdc4a93ac619ca9f9b9d6 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 18 May 2021 16:27:01 -0500 Subject: [PATCH 115/280] Fix duplicate time in GUIUtil::dateTimeStr() The update to remove deprecated Qt::SystemLocaleShortDate caused this function to display two time components. --- src/qt/guiutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 8c65c95b78..3027c27c21 100755 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -42,7 +42,7 @@ namespace GUIUtil { QString dateTimeStr(const QDateTime &date) { - return QLocale::system().toString(date, QLocale::ShortFormat) + QString(" ") + date.toString("hh:mm"); + return QLocale::system().toString(date.date(), QLocale::ShortFormat) + QString(" ") + date.toString("hh:mm"); } QString dateTimeStr(qint64 nTime) From 50577b5b4881b833f4cb98e09eb11d38ec20084e Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 13 May 2021 08:47:32 -0500 Subject: [PATCH 116/280] Refresh GUI transaction history page design This updates the GUI transaction history page to match the proposed redesign: - Add a header bar with address/label filter input - Update colors, layout, sizing, and spacing --- src/qt/bitcoingui.cpp | 8 +- src/qt/bitcoingui.h | 3 +- src/qt/res/stylesheets/dark_stylesheet.qss | 13 +- src/qt/res/stylesheets/light_stylesheet.qss | 13 +- src/qt/transactionview.cpp | 125 ++++++++++++-------- src/qt/transactionview.h | 4 +- 6 files changed, 102 insertions(+), 64 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d8b22407d9..6ff58bf6df 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -183,11 +183,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create tabs overviewPage = new OverviewPage(); - transactionsPage = new QWidget(this); - QVBoxLayout *vbox = new QVBoxLayout(); transactionView = new TransactionView(this); - vbox->addWidget(transactionView); - transactionsPage->setLayout(vbox); addressBookPage = new QWidget(this); addressBook = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); @@ -205,7 +201,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): centralWidget = new QStackedWidget(this); centralWidget->addWidget(overviewPage); - centralWidget->addWidget(transactionsPage); + centralWidget->addWidget(transactionView); centralWidget->addWidget(addressBookPage); centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(sendCoinsPage); @@ -1277,7 +1273,7 @@ void BitcoinGUI::gotoOverviewPage() void BitcoinGUI::gotoHistoryPage() { historyAction->setChecked(true); - centralWidget->setCurrentWidget(transactionsPage); + centralWidget->setCurrentWidget(transactionView); exportAction->setEnabled(true); disconnect(exportAction, SIGNAL(triggered()), 0, 0); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 6913a6f4d8..41771acf41 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -79,10 +79,10 @@ class BitcoinGUI : public QMainWindow QStackedWidget *centralWidget; OverviewPage *overviewPage; - QWidget *transactionsPage; QWidget *addressBookPage; ReceiveCoinsPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; + TransactionView *transactionView; VotingDialog *votingPage; SignVerifyMessageDialog *signVerifyMessageDialog; std::unique_ptr updateMessageDialog; @@ -135,7 +135,6 @@ class BitcoinGUI : public QMainWindow QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; Notificator *notificator; - TransactionView *transactionView; AddressBookPage *addressBook; RPCConsole *rpcConsole; DiagnosticsDialog *diagnosticsDialog; diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index cd102fe68a..ae25e67fdd 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -37,8 +37,7 @@ QTreeWidget { .QFrame, QGroupBox, QTabWidget::pane, -#SendCoinsEntry, -TransactionView { +#SendCoinsEntry { background: rgb(23, 30, 40); border: none; border-radius: 0.26em; @@ -613,6 +612,7 @@ QStatusBar QToolTip { } #currentPollsFrame, +#historyTableFrame, #recentTransactionsFrame, #researcherFrame, #SendCoinsEntry, @@ -772,6 +772,15 @@ QStatusBar QToolTip { font-weight:bold; } +/* History page */ + +TransactionView #filterFrame { + background: rgba(0, 0, 0, 0.1); + border-bottom: 0.065em solid rgba(0, 0, 0, 0.3); + border-radius: 0; + padding: 0.5em 1em; +} + /* options dialog */ #OptionsDialog #statusLabel{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 5e32010ac3..a7bc914c6b 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -33,8 +33,7 @@ QTreeWidget { .QFrame, QGroupBox, QTabWidget::pane, -#SendCoinsEntry, -TransactionView { +#SendCoinsEntry { background: white; border: none; border-radius: 0.26em; @@ -616,6 +615,7 @@ QStatusBar .QFrame QLabel { } #currentPollsFrame, +#historyTableFrame, #recentTransactionsFrame, #researcherFrame, #SendCoinsEntry, @@ -769,6 +769,15 @@ QStatusBar .QFrame QLabel { font-weight:bold; } +/* History page */ + +TransactionView #filterFrame { + background: rgb(244, 247, 249); + border-bottom: 0.065em solid rgba(0, 0, 0, 0.1); + border-radius: 0; + padding: 0.5em 1em; +} + /* options dialog */ #OptionsDialog #statusLabel{ diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 429ad9b6f6..1fae11f68b 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -11,6 +11,7 @@ #include "editaddressdialog.h" #include "optionsmodel.h" #include "guiutil.h" +#include "qt/decoration.h" #include #include @@ -29,24 +30,43 @@ #include #include -TransactionView::TransactionView(QWidget *parent) : - QFrame(parent), model(0), transactionProxyModel(0), - transactionView(0) +TransactionView::TransactionView(QWidget *parent) + : QFrame(parent) + , model(nullptr) + , transactionProxyModel(nullptr) + , transactionView(nullptr) + , searchWidgetIconAction(new QAction()) { - // Build filter row - setContentsMargins(0,0,0,0); + setContentsMargins(0, 0, 0, 0); + + // Build header + QHBoxLayout *headerFrameLayout = new QHBoxLayout(); + headerFrameLayout->setContentsMargins(0, 0, 0, 0); + headerFrameLayout->setSpacing(15); + + QLabel *headerTitleLabel = new QLabel(tr("Transaction History")); + headerTitleLabel->setObjectName("headerTitleLabel"); + GRC::ScaleFontPointSize(headerTitleLabel, 15); + headerFrameLayout->addWidget(headerTitleLabel); - QHBoxLayout *hlayout = new QHBoxLayout(); - hlayout->setContentsMargins(0,0,0,0); - hlayout->setSpacing(5); - hlayout->addSpacing(26); + headerFrameLayout->addStretch(); + + searchWidget = new QLineEdit(this); + searchWidget->setPlaceholderText(tr("Search by address or label")); + searchWidget->addAction(searchWidgetIconAction, QLineEdit::LeadingPosition); + searchWidget->setClearButtonEnabled(true); + headerFrameLayout->addWidget(searchWidget); + + QFrame *headerFrame = new QFrame(this); + headerFrame->setObjectName("headerFrame"); + headerFrame->setLayout(headerFrameLayout); + + // Build filter row + QHBoxLayout *filterFrameLayout = new QHBoxLayout(); + filterFrameLayout->setContentsMargins(0, 0, 0, 0); + filterFrameLayout->setSpacing(6); dateWidget = new QComboBox(this); -#ifdef Q_OS_MAC - dateWidget->setFixedWidth(121); -#else - dateWidget->setFixedWidth(120); -#endif dateWidget->addItem(tr("All Time"), All); dateWidget->addItem(tr("Today"), Today); dateWidget->addItem(tr("This week"), ThisWeek); @@ -54,15 +74,9 @@ TransactionView::TransactionView(QWidget *parent) : dateWidget->addItem(tr("Last month"), LastMonth); dateWidget->addItem(tr("This year"), ThisYear); dateWidget->addItem(tr("Range..."), Range); - hlayout->addWidget(dateWidget); + filterFrameLayout->addWidget(dateWidget); typeWidget = new QComboBox(this); -#ifdef Q_OS_MAC - typeWidget->setFixedWidth(121); -#else - typeWidget->setFixedWidth(120); -#endif - typeWidget->addItem(tr("All Types"), TransactionFilterProxy::ALL_TYPES); typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) | TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther)); @@ -71,49 +85,46 @@ TransactionView::TransactionView(QWidget *parent) : typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); + filterFrameLayout->addWidget(typeWidget); - hlayout->addWidget(typeWidget); - - addressWidget = new QLineEdit(this); - /* TODO: Move this to the XML file */ - addressWidget->setPlaceholderText(tr("Enter address or label to search")); - hlayout->addWidget(addressWidget); + filterFrameLayout->addStretch(); amountWidget = new QLineEdit(this); - /* TODO: Move this to the XML file */ amountWidget->setPlaceholderText(tr("Min amount")); - -#ifdef Q_OS_MAC - amountWidget->setFixedWidth(97); -#else - amountWidget->setFixedWidth(100); -#endif amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); - hlayout->addWidget(amountWidget); + QSizePolicy amountWidgetSizePolicy = amountWidget->sizePolicy(); + amountWidgetSizePolicy.setHorizontalPolicy(QSizePolicy::Minimum); + amountWidget->setSizePolicy(amountWidgetSizePolicy); + filterFrameLayout->addWidget(amountWidget); - QVBoxLayout *vlayout = new QVBoxLayout(this); - vlayout->setContentsMargins(0,0,0,0); + QFrame *filterFrame = new QFrame(this); + filterFrame->setObjectName("filterFrame"); + filterFrame->setLayout(filterFrameLayout); QTableView *view = new QTableView(this); - vlayout->addLayout(hlayout); - vlayout->addWidget(createDateRangeWidget()); - vlayout->addWidget(view); - vlayout->setSpacing(5); - int width = view->verticalScrollBar()->sizeHint().width(); - // Cover scroll bar width with spacing -#ifdef Q_OS_MAC - hlayout->addSpacing(width+2); -#else - hlayout->addSpacing(width); -#endif - // Always show scroll bar view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); view->setTabKeyNavigation(false); view->setContextMenuPolicy(Qt::CustomContextMenu); view->setShowGrid(false); - + view->horizontalHeader()->setHighlightSections(false); transactionView = view; + QVBoxLayout *tableViewLayout = new QVBoxLayout(); + tableViewLayout->setContentsMargins(9, 9, 9, 9); + QFrame *tableViewFrame = new QFrame(this); + tableViewFrame->setLayout(new QVBoxLayout()); + tableViewFrame->layout()->setContentsMargins(0, 0, 0, 0); + tableViewFrame->layout()->addWidget(view); + tableViewLayout->addWidget(tableViewFrame); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->setContentsMargins(0, 0, 0, 0); + vlayout->setSpacing(0); + vlayout->addWidget(headerFrame); + vlayout->addWidget(filterFrame); + vlayout->addWidget(createDateRangeWidget()); + vlayout->addLayout(tableViewLayout); + // Actions QAction *copyAddressAction = new QAction(tr("Copy address"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this); @@ -133,7 +144,7 @@ TransactionView::TransactionView(QWidget *parent) : // Connect actions connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); + connect(searchWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); @@ -179,6 +190,13 @@ void TransactionView::setModel(WalletModel *model) transactionView->horizontalHeader()->resizeSection( TransactionTableModel::Amount, 100); } + + if (model && model->getOptionsModel()) { + connect( + model->getOptionsModel(), SIGNAL(walletStylesheetChanged(QString)), + this, SLOT(updateIcons(QString))); + updateIcons(model->getOptionsModel()->getCurrentStyle()); + } } void TransactionView::chooseDate(int idx) @@ -429,3 +447,8 @@ void TransactionView::focusTransaction(const QModelIndex &idx) transactionView->setCurrentIndex(targetIdx); transactionView->setFocus(); } + +void TransactionView::updateIcons(const QString& theme) +{ + searchWidgetIconAction->setIcon(QIcon(":/icons/" + theme + "_search")); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index a256c8c63e..7f1bead3e4 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -46,7 +46,8 @@ class TransactionView : public QFrame QComboBox *dateWidget; QComboBox *typeWidget; - QLineEdit *addressWidget; + QLineEdit *searchWidget; + QAction *searchWidgetIconAction; QLineEdit *amountWidget; QMenu *contextMenu; @@ -66,6 +67,7 @@ private slots: void copyLabel(); void copyAmount(); void copyTxID(); + void updateIcons(const QString& theme); signals: void doubleClicked(const QModelIndex&); From 2b338e031d19fd23ae3433c6c2ca35639fd0f64b Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 20 May 2021 02:54:19 -0500 Subject: [PATCH 117/280] Fix GUI debug console traffic graph legend colors The stylesheets override the custom palette colors for line widgets. This sets the colors for the traffic graph legend with style rules. --- src/qt/res/stylesheets/dark_stylesheet.qss | 10 ++++++++++ src/qt/res/stylesheets/light_stylesheet.qss | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index cd102fe68a..a78349d288 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -803,3 +803,13 @@ QStatusBar QToolTip { #consolidateSendReadyLabel{ color: rgb(55, 250, 55); } + +/* Debug Console */ + +RPCConsole #greenLine { + border-top-color: rgb(0, 255, 0); +} + +RPCConsole #redLine { + border-top-color: rgb(255, 0, 0); +} diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 5e32010ac3..a9f9dfab18 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -800,3 +800,13 @@ QStatusBar .QFrame QLabel { #consolidateSendReadyLabel{ color: rgb(0, 128, 0); } + +/* Debug Console */ + +RPCConsole #greenLine { + border-top-color: rgb(0, 255, 0); +} + +RPCConsole #redLine { + border-top-color: rgb(255, 0, 0); +} From 379e257fa1bca6bf0642626b2c5da516ffe689dc Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 18 May 2021 17:42:11 -0500 Subject: [PATCH 118/280] Add empty placeholder to GUI recent transactions list This adds a presentation nicety to the recent transactions list on the overview page that shows an empty placeholder text and icon in wallets with no transactions. It is a bit of polish that fills the empty space that new users see with a fresh installation. The placeholder is a widget that we can reuse in other components when we need to display a "nothing here" or "no results" message. --- gridcoinresearch.pro | 7 ++- src/Makefile.qt.include | 5 ++ src/qt/bitcoin.qrc | 1 + src/qt/forms/noresult.ui | 66 +++++++++++++++++++++ src/qt/forms/overviewpage.ui | 16 +++++ src/qt/noresult.cpp | 47 +++++++++++++++ src/qt/noresult.h | 33 +++++++++++ src/qt/overviewpage.cpp | 10 ++++ src/qt/res/icons/no_result.svg | 3 + src/qt/res/stylesheets/dark_stylesheet.qss | 13 ++++ src/qt/res/stylesheets/light_stylesheet.qss | 13 ++++ 11 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/qt/forms/noresult.ui create mode 100644 src/qt/noresult.cpp create mode 100644 src/qt/noresult.h create mode 100644 src/qt/res/icons/no_result.svg diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 79f85ade05..03331f5158 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -273,7 +273,8 @@ HEADERS += src/qt/bitcoingui.h \ src/backup.h \ src/appcache.h \ src/grcrestarter.h \ - src/qt/clicklabel.h + src/qt/clicklabel.h \ + src/qt/noresult.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ @@ -368,7 +369,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/backup.cpp \ src/appcache.cpp \ src/grcrestarter.cpp \ - src/qt/clicklabel.cpp + src/qt/clicklabel.cpp \ + src/qt/noresult.cpp ## #RC_FILE = qaxserver.rc @@ -379,6 +381,7 @@ RESOURCES += \ FORMS += \ src/qt/forms/coincontroldialog.ui \ + src/qt/forms/noresult.ui \ src/qt/forms/researcherwizard.ui \ src/qt/forms/researcherwizardauthpage.ui \ src/qt/forms/researcherwizardbeaconpage.ui \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 46b5b19af0..bcd333a036 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -85,6 +85,7 @@ QT_FORMS_UI = \ qt/forms/signverifymessagedialog.ui \ qt/forms/addressbookpage.ui \ qt/forms/editaddressdialog.ui \ + qt/forms/noresult.ui \ qt/forms/overviewpage.ui \ qt/forms/receivecoinspage.ui \ qt/forms/researcherwizard.ui \ @@ -130,6 +131,7 @@ QT_MOC_CPP = \ qt/moc_macdockiconhandler.cpp \ qt/moc_macnotificationhandler.cpp \ qt/moc_monitoreddatamapper.cpp \ + qt/moc_noresult.cpp \ qt/moc_notificator.cpp \ qt/moc_optionsdialog.cpp \ qt/moc_optionsmodel.cpp \ @@ -209,6 +211,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/macnotificationhandler.h \ qt/macos_appnap.h \ qt/monitoreddatamapper.h \ + qt/noresult.h \ qt/notificator.h \ qt/optionsdialog.h \ qt/optionsmodel.h \ @@ -273,6 +276,7 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/guiutil.cpp \ qt/intro.cpp \ qt/monitoreddatamapper.cpp \ + qt/noresult.cpp \ qt/notificator.cpp \ qt/optionsdialog.cpp \ qt/optionsmodel.cpp \ @@ -343,6 +347,7 @@ RES_ICONS = \ qt/res/icons/menu.svg \ qt/res/icons/menu_active.svg \ qt/res/icons/message.svg \ + qt/res/icons/no_result.svg \ qt/res/icons/qrcode.png \ qt/res/icons/quit.png \ qt/res/icons/remove.png \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index ed296ff2dc..d72c35728a 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -179,6 +179,7 @@ res/icons/dark_mode_active.svg res/icons/light_mode.svg res/icons/light_mode_active.svg + res/icons/no_result.svg
diff --git a/src/qt/forms/noresult.ui b/src/qt/forms/noresult.ui new file mode 100644 index 0000000000..ac6060bdfc --- /dev/null +++ b/src/qt/forms/noresult.ui @@ -0,0 +1,66 @@ + + + NoResult + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + Form + + + + 16 + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + Nothing here yet... + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 3ac9045a85..0e9380de30 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -1020,6 +1020,16 @@ + + + + + 0 + 100 + + + + @@ -1077,6 +1087,12 @@ QLabel
clicklabel.h
+ + NoResult + QWidget +
noresult.h
+ 1 +
diff --git a/src/qt/noresult.cpp b/src/qt/noresult.cpp new file mode 100644 index 0000000000..0eddffd745 --- /dev/null +++ b/src/qt/noresult.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/ui_noresult.h" +#include "qt/noresult.h" + +NoResult::NoResult(QWidget *parent) + : QWidget(parent) + , ui(new Ui::NoResult) + , m_content_widget(nullptr) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->titleLabel, 13); +} + +NoResult::~NoResult() +{ + delete ui; +} + +QWidget* NoResult::contentWidget() +{ + return m_content_widget; +} + +void NoResult::setTitle(const QString& title) +{ + ui->titleLabel->setText(title); +} + +void NoResult::setContentWidget(QWidget* widget) +{ + if (m_content_widget != nullptr) { + ui->verticalLayout->removeWidget(m_content_widget); + } + + m_content_widget = widget; + + if (widget != nullptr) { + // Insert the widget above the bottom spacer: + const int index = ui->verticalLayout->count() - 1; + ui->verticalLayout->insertWidget(index, widget, 0, Qt::AlignHCenter); + } +} diff --git a/src/qt/noresult.h b/src/qt/noresult.h new file mode 100644 index 0000000000..3be380156c --- /dev/null +++ b/src/qt/noresult.h @@ -0,0 +1,33 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef NORESULT_H +#define NORESULT_H + +#include + +namespace Ui { +class NoResult; +} + +class NoResult : public QWidget +{ + Q_OBJECT + +public: + explicit NoResult(QWidget *parent = nullptr); + ~NoResult(); + + QWidget* contentWidget(); + +public slots: + void setTitle(const QString& title); + void setContentWidget(QWidget* widget); + +private: + Ui::NoResult *ui; + QWidget* m_content_widget; +}; + +#endif // NORESULT_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index ad0fcb6522..6ee32b184b 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -209,6 +209,16 @@ void OverviewPage::updateTransactions() { int numItems = getNumTransactionsForView(); + // When we receive our first transaction, we can free the memory used + // for the "nothing here yet" placeholder in the transaction list. It + // will never appear again: + // + if (numItems > 0) + { + delete ui->recentTransactionsNoResult; + ui->recentTransactionsNoResult = nullptr; + } + LogPrint(BCLog::LogFlags::QT, "OverviewPage::updateTransactions(): numItems = %d, getLimit() = %d", numItems, filter->getLimit()); // This is a "stairstep" approach, using x3 to x6 factors to size the setLimit. diff --git a/src/qt/res/icons/no_result.svg b/src/qt/res/icons/no_result.svg new file mode 100644 index 0000000000..80172e8260 --- /dev/null +++ b/src/qt/res/icons/no_result.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index cd102fe68a..f5ad2f31f0 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -579,6 +579,19 @@ QStatusBar QToolTip { max-height:9.225em; } +NoResult #iconLabel { + image: url(:/icons/no_result); + min-width: 2em; + max-width: 2em; + min-height: 2em; + max-height: 2em; +} + +NoResult #titleLabel { + color: rgba(204, 208, 209, 0.6); + font-weight: 500; +} + #headerFrame { background: none; border-bottom: 0.065em solid rgb(18, 26, 34); diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 5e32010ac3..ed472a26f7 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -582,6 +582,19 @@ QStatusBar .QFrame QLabel { max-height:9.225em; } +NoResult #iconLabel { + image: url(:/icons/no_result); + min-width: 2em; + max-width: 2em; + min-height: 2em; + max-height: 2em; +} + +NoResult #titleLabel { + color: rgba(58, 70, 93, 0.6); + font-weight: 500; +} + #headerFrame { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(240, 240, 240), stop: 0.1 rgb(255, 255, 255)); border-bottom: 0.065em solid rgb(230, 230, 230); From a5e773c8a993f43bfbcd55696bf2b54e55cd57bb Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 13 May 2021 08:49:11 -0500 Subject: [PATCH 119/280] Reword history page address search field placeholder This updates the GUI localizations for the address/label search field placeholder on the transaction history page to match proposed text in the new design. The placeholder now contains the same text as that in the address book filter fields, so we can reuse existing translations for this text in three places: - The "receive payment" page - The "transaction history" page - The "favorites" (address book) page --- src/qt/locale/bitcoin_af_ZA.ts | 2 +- src/qt/locale/bitcoin_ar.ts | 2 +- src/qt/locale/bitcoin_be_BY.ts | 2 +- src/qt/locale/bitcoin_bg.ts | 2 +- src/qt/locale/bitcoin_bs.ts | 2 +- src/qt/locale/bitcoin_ca.ts | 2 +- src/qt/locale/bitcoin_ca@valencia.ts | 2 +- src/qt/locale/bitcoin_ca_ES.ts | 2 +- src/qt/locale/bitcoin_cs.ts | 2 +- src/qt/locale/bitcoin_cy.ts | 2 +- src/qt/locale/bitcoin_da.ts | 2 +- src/qt/locale/bitcoin_de.ts | 2 +- src/qt/locale/bitcoin_el_GR.ts | 2 +- src/qt/locale/bitcoin_en.ts | 2 +- src/qt/locale/bitcoin_eo.ts | 2 +- src/qt/locale/bitcoin_es.ts | 2 +- src/qt/locale/bitcoin_es_CL.ts | 2 +- src/qt/locale/bitcoin_es_DO.ts | 2 +- src/qt/locale/bitcoin_es_MX.ts | 2 +- src/qt/locale/bitcoin_es_UY.ts | 2 +- src/qt/locale/bitcoin_et.ts | 2 +- src/qt/locale/bitcoin_eu_ES.ts | 2 +- src/qt/locale/bitcoin_fa.ts | 2 +- src/qt/locale/bitcoin_fa_IR.ts | 2 +- src/qt/locale/bitcoin_fi.ts | 2 +- src/qt/locale/bitcoin_fr.ts | 2 +- src/qt/locale/bitcoin_fr_CA.ts | 2 +- src/qt/locale/bitcoin_gl.ts | 2 +- src/qt/locale/bitcoin_he.ts | 2 +- src/qt/locale/bitcoin_hi_IN.ts | 2 +- src/qt/locale/bitcoin_hr.ts | 2 +- src/qt/locale/bitcoin_hu.ts | 2 +- src/qt/locale/bitcoin_id_ID.ts | 2 +- src/qt/locale/bitcoin_it.ts | 2 +- src/qt/locale/bitcoin_ja.ts | 2 +- src/qt/locale/bitcoin_ka.ts | 2 +- src/qt/locale/bitcoin_kk_KZ.ts | 2 +- src/qt/locale/bitcoin_ko_KR.ts | 2 +- src/qt/locale/bitcoin_ky.ts | 2 +- src/qt/locale/bitcoin_la.ts | 2 +- src/qt/locale/bitcoin_lt.ts | 2 +- src/qt/locale/bitcoin_lv_LV.ts | 2 +- src/qt/locale/bitcoin_ms_MY.ts | 2 +- src/qt/locale/bitcoin_nb.ts | 2 +- src/qt/locale/bitcoin_nl.ts | 2 +- src/qt/locale/bitcoin_pam.ts | 2 +- src/qt/locale/bitcoin_pl.ts | 2 +- src/qt/locale/bitcoin_pt_BR.ts | 2 +- src/qt/locale/bitcoin_pt_PT.ts | 2 +- src/qt/locale/bitcoin_ro_RO.ts | 2 +- src/qt/locale/bitcoin_ru.ts | 2 +- src/qt/locale/bitcoin_sk.ts | 2 +- src/qt/locale/bitcoin_sl_SI.ts | 2 +- src/qt/locale/bitcoin_sq.ts | 2 +- src/qt/locale/bitcoin_sr.ts | 2 +- src/qt/locale/bitcoin_sv.ts | 2 +- src/qt/locale/bitcoin_th_TH.ts | 2 +- src/qt/locale/bitcoin_tr.ts | 2 +- src/qt/locale/bitcoin_uk.ts | 2 +- src/qt/locale/bitcoin_ur_PK.ts | 2 +- src/qt/locale/bitcoin_vi.ts | 2 +- src/qt/locale/bitcoin_vi_VN.ts | 2 +- src/qt/locale/bitcoin_zh_CN.ts | 2 +- src/qt/locale/bitcoin_zh_TW.ts | 2 +- 64 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/qt/locale/bitcoin_af_ZA.ts b/src/qt/locale/bitcoin_af_ZA.ts index c00933b8c8..1e4f6bb02a 100644 --- a/src/qt/locale/bitcoin_af_ZA.ts +++ b/src/qt/locale/bitcoin_af_ZA.ts @@ -4250,7 +4250,7 @@ Dit beteken dat 'n fooi van ten minste %2 word benodig. - Enter address or label to search + Search by address or label Voer adres of etiket om te soek diff --git a/src/qt/locale/bitcoin_ar.ts b/src/qt/locale/bitcoin_ar.ts index 9648dcb00e..a83bc9d61c 100644 --- a/src/qt/locale/bitcoin_ar.ts +++ b/src/qt/locale/bitcoin_ar.ts @@ -4218,7 +4218,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label ادخل عنوان أووصف للبحث diff --git a/src/qt/locale/bitcoin_be_BY.ts b/src/qt/locale/bitcoin_be_BY.ts index 9bd68e60a2..8e0f516c94 100644 --- a/src/qt/locale/bitcoin_be_BY.ts +++ b/src/qt/locale/bitcoin_be_BY.ts @@ -4188,7 +4188,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Увядзіце адрас ці пазнаку для пошуку diff --git a/src/qt/locale/bitcoin_bg.ts b/src/qt/locale/bitcoin_bg.ts index 27a1f371b9..77a904a492 100644 --- a/src/qt/locale/bitcoin_bg.ts +++ b/src/qt/locale/bitcoin_bg.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Търсене по адрес или име diff --git a/src/qt/locale/bitcoin_bs.ts b/src/qt/locale/bitcoin_bs.ts index fdca0f383e..2987ac8812 100644 --- a/src/qt/locale/bitcoin_bs.ts +++ b/src/qt/locale/bitcoin_bs.ts @@ -4188,7 +4188,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_ca.ts b/src/qt/locale/bitcoin_ca.ts index 1bfa06ae81..8f66ca17eb 100644 --- a/src/qt/locale/bitcoin_ca.ts +++ b/src/qt/locale/bitcoin_ca.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Introduïu una adreça o una etiqueta per cercar diff --git a/src/qt/locale/bitcoin_ca@valencia.ts b/src/qt/locale/bitcoin_ca@valencia.ts index fcedcc9657..9918c1b120 100644 --- a/src/qt/locale/bitcoin_ca@valencia.ts +++ b/src/qt/locale/bitcoin_ca@valencia.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Introduïu una adreça o una etiqueta per cercar diff --git a/src/qt/locale/bitcoin_ca_ES.ts b/src/qt/locale/bitcoin_ca_ES.ts index 896f8dc78c..5a803d628f 100644 --- a/src/qt/locale/bitcoin_ca_ES.ts +++ b/src/qt/locale/bitcoin_ca_ES.ts @@ -4192,7 +4192,7 @@ En aquest cas es requereix una comisió d'almenys 2%. - Enter address or label to search + Search by address or label Introduïu una adreça o una etiqueta per cercar diff --git a/src/qt/locale/bitcoin_cs.ts b/src/qt/locale/bitcoin_cs.ts index 9b1d7416d7..c7777775d7 100644 --- a/src/qt/locale/bitcoin_cs.ts +++ b/src/qt/locale/bitcoin_cs.ts @@ -4191,7 +4191,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Zadej adresu nebo označení pro její vyhledání diff --git a/src/qt/locale/bitcoin_cy.ts b/src/qt/locale/bitcoin_cy.ts index ce84a08612..1d2adc6c69 100644 --- a/src/qt/locale/bitcoin_cy.ts +++ b/src/qt/locale/bitcoin_cy.ts @@ -4208,7 +4208,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index b5596163ac..2c997c5710 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -4195,7 +4195,7 @@ Det betyder, at et gebyr på mindst %2 er påkrævet. - Enter address or label to search + Search by address or label Indtast adresse eller mærkat for at søge diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index bdcbf5609e..1522671673 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -4197,7 +4197,7 @@ Dieses Label wird rot, wenn die Priorität kleiner ist als Mittel. - Enter address or label to search + Search by address or label Zu suchende Adresse oder Bezeichnung eingeben diff --git a/src/qt/locale/bitcoin_el_GR.ts b/src/qt/locale/bitcoin_el_GR.ts index 2e8cf17d58..358033c1e1 100644 --- a/src/qt/locale/bitcoin_el_GR.ts +++ b/src/qt/locale/bitcoin_el_GR.ts @@ -4224,7 +4224,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index cc69ea1861..36dcc0e738 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -4181,7 +4181,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_eo.ts b/src/qt/locale/bitcoin_eo.ts index 2138e45e44..c15dc9798b 100644 --- a/src/qt/locale/bitcoin_eo.ts +++ b/src/qt/locale/bitcoin_eo.ts @@ -4196,7 +4196,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Tajpu adreson a? etikedon por ser?i diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index 610663bb9f..2a19a549c3 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -4197,7 +4197,7 @@ Esto significa que se requiere una cuota de al menos %2. - Enter address or label to search + Search by address or label Introduzca una dirección o etiqueta que buscar diff --git a/src/qt/locale/bitcoin_es_CL.ts b/src/qt/locale/bitcoin_es_CL.ts index 030bbcdd14..1e6bd7b74c 100644 --- a/src/qt/locale/bitcoin_es_CL.ts +++ b/src/qt/locale/bitcoin_es_CL.ts @@ -4184,7 +4184,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Introduce una dirección o etiqueta para buscar diff --git a/src/qt/locale/bitcoin_es_DO.ts b/src/qt/locale/bitcoin_es_DO.ts index 0702b3e2d4..2e222a7d5e 100644 --- a/src/qt/locale/bitcoin_es_DO.ts +++ b/src/qt/locale/bitcoin_es_DO.ts @@ -4181,7 +4181,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Introduzca una dirección o etiqueta que buscar diff --git a/src/qt/locale/bitcoin_es_MX.ts b/src/qt/locale/bitcoin_es_MX.ts index 13b75c0479..6c8fc62110 100644 --- a/src/qt/locale/bitcoin_es_MX.ts +++ b/src/qt/locale/bitcoin_es_MX.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Ingrese dirección o capa a buscar diff --git a/src/qt/locale/bitcoin_es_UY.ts b/src/qt/locale/bitcoin_es_UY.ts index 2cfab5c0c7..353f829584 100644 --- a/src/qt/locale/bitcoin_es_UY.ts +++ b/src/qt/locale/bitcoin_es_UY.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_et.ts b/src/qt/locale/bitcoin_et.ts index 0db8da6558..55f6ae0145 100644 --- a/src/qt/locale/bitcoin_et.ts +++ b/src/qt/locale/bitcoin_et.ts @@ -4181,7 +4181,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Otsimiseks sisesta märgis või aadress diff --git a/src/qt/locale/bitcoin_eu_ES.ts b/src/qt/locale/bitcoin_eu_ES.ts index 77799e0e52..5ffe5e700b 100644 --- a/src/qt/locale/bitcoin_eu_ES.ts +++ b/src/qt/locale/bitcoin_eu_ES.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Sartu bilatzeko helbide edo etiketa diff --git a/src/qt/locale/bitcoin_fa.ts b/src/qt/locale/bitcoin_fa.ts index d52986a214..c61e1909f6 100644 --- a/src/qt/locale/bitcoin_fa.ts +++ b/src/qt/locale/bitcoin_fa.ts @@ -4173,7 +4173,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_fa_IR.ts b/src/qt/locale/bitcoin_fa_IR.ts index 938bf7b12f..5a78668afb 100644 --- a/src/qt/locale/bitcoin_fa_IR.ts +++ b/src/qt/locale/bitcoin_fa_IR.ts @@ -4198,7 +4198,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_fi.ts b/src/qt/locale/bitcoin_fi.ts index 0786350cb2..eaeaf0a559 100644 --- a/src/qt/locale/bitcoin_fi.ts +++ b/src/qt/locale/bitcoin_fi.ts @@ -4215,7 +4215,7 @@ Tämä tarkoittaa, että ainakin %2 rahansiirtopalkkio tarvitaan. - Enter address or label to search + Search by address or label Anna etsittävä osoite tai tunniste diff --git a/src/qt/locale/bitcoin_fr.ts b/src/qt/locale/bitcoin_fr.ts index f9ce7def5c..6536b11f66 100644 --- a/src/qt/locale/bitcoin_fr.ts +++ b/src/qt/locale/bitcoin_fr.ts @@ -4195,7 +4195,7 @@ Cela implique que des frais à hauteur d'au moins %2 seront nécessaires. - Enter address or label to search + Search by address or label Saisir une adresse ou une étiquette à rechercher diff --git a/src/qt/locale/bitcoin_fr_CA.ts b/src/qt/locale/bitcoin_fr_CA.ts index 71f2c1f0fe..9a478144a2 100644 --- a/src/qt/locale/bitcoin_fr_CA.ts +++ b/src/qt/locale/bitcoin_fr_CA.ts @@ -4195,7 +4195,7 @@ Les montants inférieurs à 0.546 fois les frais minimum de relais apparaissent - Enter address or label to search + Search by address or label Saisir une adresse ou une étiquette à rechercher diff --git a/src/qt/locale/bitcoin_gl.ts b/src/qt/locale/bitcoin_gl.ts index 8207dd7a96..57f29929f6 100644 --- a/src/qt/locale/bitcoin_gl.ts +++ b/src/qt/locale/bitcoin_gl.ts @@ -4181,7 +4181,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Introduce dirección ou etiqueta para buscar diff --git a/src/qt/locale/bitcoin_he.ts b/src/qt/locale/bitcoin_he.ts index 5a79eed0a1..2b8c55ca65 100644 --- a/src/qt/locale/bitcoin_he.ts +++ b/src/qt/locale/bitcoin_he.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_hi_IN.ts b/src/qt/locale/bitcoin_hi_IN.ts index 2249448329..f52ca4c8bc 100644 --- a/src/qt/locale/bitcoin_hi_IN.ts +++ b/src/qt/locale/bitcoin_hi_IN.ts @@ -4182,7 +4182,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_hr.ts b/src/qt/locale/bitcoin_hr.ts index 85d161b74e..4d67fb5b46 100644 --- a/src/qt/locale/bitcoin_hr.ts +++ b/src/qt/locale/bitcoin_hr.ts @@ -4191,7 +4191,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Unesite adresu ili oznaku za pretraživanje diff --git a/src/qt/locale/bitcoin_hu.ts b/src/qt/locale/bitcoin_hu.ts index f169ef731c..b24e1d68d7 100644 --- a/src/qt/locale/bitcoin_hu.ts +++ b/src/qt/locale/bitcoin_hu.ts @@ -4177,7 +4177,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Írd be a keresendő címet vagy címkét diff --git a/src/qt/locale/bitcoin_id_ID.ts b/src/qt/locale/bitcoin_id_ID.ts index ba894a15be..53f124cec3 100644 --- a/src/qt/locale/bitcoin_id_ID.ts +++ b/src/qt/locale/bitcoin_id_ID.ts @@ -4206,7 +4206,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Masukkan alamat atau label untuk mencari diff --git a/src/qt/locale/bitcoin_it.ts b/src/qt/locale/bitcoin_it.ts index a935366080..2db3436ac3 100644 --- a/src/qt/locale/bitcoin_it.ts +++ b/src/qt/locale/bitcoin_it.ts @@ -4195,7 +4195,7 @@ Questa etichetta diventa rossa se la priorità è minore di "media". - Enter address or label to search + Search by address or label Inserisci un indirizzo o un'etichetta da cercare diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 1e984803b6..1a77b651c1 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -4168,7 +4168,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label 検索するアドレスまたはラベルを入力 diff --git a/src/qt/locale/bitcoin_ka.ts b/src/qt/locale/bitcoin_ka.ts index 09a2f3ceae..4291462b7f 100644 --- a/src/qt/locale/bitcoin_ka.ts +++ b/src/qt/locale/bitcoin_ka.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_kk_KZ.ts b/src/qt/locale/bitcoin_kk_KZ.ts index f91970881c..47b542c7de 100644 --- a/src/qt/locale/bitcoin_kk_KZ.ts +++ b/src/qt/locale/bitcoin_kk_KZ.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index 07d45f2a2f..7594992ffd 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -4168,7 +4168,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_ky.ts b/src/qt/locale/bitcoin_ky.ts index 79727f93a1..c50b6c679c 100644 --- a/src/qt/locale/bitcoin_ky.ts +++ b/src/qt/locale/bitcoin_ky.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_la.ts b/src/qt/locale/bitcoin_la.ts index 35a617ca86..c612d05950 100644 --- a/src/qt/locale/bitcoin_la.ts +++ b/src/qt/locale/bitcoin_la.ts @@ -4181,7 +4181,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Insere inscriptionem vel titulum ut quaeras diff --git a/src/qt/locale/bitcoin_lt.ts b/src/qt/locale/bitcoin_lt.ts index 6e310734c7..8d4ed5d853 100644 --- a/src/qt/locale/bitcoin_lt.ts +++ b/src/qt/locale/bitcoin_lt.ts @@ -4191,7 +4191,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Įveskite adresą ar žymę į paiešką diff --git a/src/qt/locale/bitcoin_lv_LV.ts b/src/qt/locale/bitcoin_lv_LV.ts index 4b35e4b95a..01e8dd35ce 100644 --- a/src/qt/locale/bitcoin_lv_LV.ts +++ b/src/qt/locale/bitcoin_lv_LV.ts @@ -4191,7 +4191,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Ierakstiet mekl?jamo nosaukumu vai adresi diff --git a/src/qt/locale/bitcoin_ms_MY.ts b/src/qt/locale/bitcoin_ms_MY.ts index 4a3efe37e6..624d09a484 100644 --- a/src/qt/locale/bitcoin_ms_MY.ts +++ b/src/qt/locale/bitcoin_ms_MY.ts @@ -4184,7 +4184,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index 7ae4755f10..2a95b08980 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -4183,7 +4183,7 @@ Dette betyr at det trengs en avgift på minimum %2. - Enter address or label to search + Search by address or label Skriv inn adresse eller merkelapp for søk diff --git a/src/qt/locale/bitcoin_nl.ts b/src/qt/locale/bitcoin_nl.ts index f50094d337..a39654f030 100644 --- a/src/qt/locale/bitcoin_nl.ts +++ b/src/qt/locale/bitcoin_nl.ts @@ -4195,7 +4195,7 @@ Dit betekend dat een fee van %2 is vereist. - Enter address or label to search + Search by address or label Vul adres of label in om te zoeken diff --git a/src/qt/locale/bitcoin_pam.ts b/src/qt/locale/bitcoin_pam.ts index f27e5581e8..a340adc33f 100644 --- a/src/qt/locale/bitcoin_pam.ts +++ b/src/qt/locale/bitcoin_pam.ts @@ -3297,7 +3297,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Magpalub kang address o label para pantunan diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index 663c7ded98..d451aa67ea 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -4209,7 +4209,7 @@ Wartości poniżej 0.546*minimalna wartość przekazu. są traktowane jako " - Enter address or label to search + Search by address or label Wprowadź adres albo etykietę żeby wyszukał diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index db92c34fab..3ceda1e9c5 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -4181,7 +4181,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Procure um endereço ou rótulo diff --git a/src/qt/locale/bitcoin_pt_PT.ts b/src/qt/locale/bitcoin_pt_PT.ts index ce569a3762..86fe47f52f 100644 --- a/src/qt/locale/bitcoin_pt_PT.ts +++ b/src/qt/locale/bitcoin_pt_PT.ts @@ -4194,7 +4194,7 @@ Isto significa que uma taxa de pelo menos %2 é necessária. - Enter address or label to search + Search by address or label Insira endereço ou etiqueta a pesquisar diff --git a/src/qt/locale/bitcoin_ro_RO.ts b/src/qt/locale/bitcoin_ro_RO.ts index 682476a54a..a8a2695d46 100644 --- a/src/qt/locale/bitcoin_ro_RO.ts +++ b/src/qt/locale/bitcoin_ro_RO.ts @@ -4205,7 +4205,7 @@ Acest lucru înseamn? c? o tax? de cel pu?in %2 este necesar? - Enter address or label to search + Search by address or label Introdu adresa sau eticheta pentru c?utare diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index a135ef7d87..31a97178f2 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -4207,7 +4207,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Введите адрес или метку для поиска diff --git a/src/qt/locale/bitcoin_sk.ts b/src/qt/locale/bitcoin_sk.ts index 5d4ff9b563..baecb3d8bc 100644 --- a/src/qt/locale/bitcoin_sk.ts +++ b/src/qt/locale/bitcoin_sk.ts @@ -4205,7 +4205,7 @@ To znamená, že je potrebný poplatok aspoň %2. - Enter address or label to search + Search by address or label Vložte adresu alebo popis pre vyh?adávanie diff --git a/src/qt/locale/bitcoin_sl_SI.ts b/src/qt/locale/bitcoin_sl_SI.ts index 1900971145..f7e0d12175 100644 --- a/src/qt/locale/bitcoin_sl_SI.ts +++ b/src/qt/locale/bitcoin_sl_SI.ts @@ -4225,7 +4225,7 @@ Ta oznaka se obarva rde?e, ?e je prioriteta manjša kot "srednja". - Enter address or label to search + Search by address or label Vnesite naslov ali oznako za iskanje diff --git a/src/qt/locale/bitcoin_sq.ts b/src/qt/locale/bitcoin_sq.ts index 642206e640..660669e4bf 100644 --- a/src/qt/locale/bitcoin_sq.ts +++ b/src/qt/locale/bitcoin_sq.ts @@ -4178,7 +4178,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_sr.ts b/src/qt/locale/bitcoin_sr.ts index ccdbd3fedf..752581cd83 100644 --- a/src/qt/locale/bitcoin_sr.ts +++ b/src/qt/locale/bitcoin_sr.ts @@ -4188,7 +4188,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index 52d538c977..bfdea92bb2 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -4196,7 +4196,7 @@ Detta betyder att en avgift på minst %2 krävs. - Enter address or label to search + Search by address or label Sök efter adress eller etikett diff --git a/src/qt/locale/bitcoin_th_TH.ts b/src/qt/locale/bitcoin_th_TH.ts index cef25c38de..16c4ee0e4d 100644 --- a/src/qt/locale/bitcoin_th_TH.ts +++ b/src/qt/locale/bitcoin_th_TH.ts @@ -4168,7 +4168,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_tr.ts b/src/qt/locale/bitcoin_tr.ts index 3a9ae78a2a..63d8331669 100644 --- a/src/qt/locale/bitcoin_tr.ts +++ b/src/qt/locale/bitcoin_tr.ts @@ -4185,7 +4185,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label Aranacak adres ya da etiket giriniz diff --git a/src/qt/locale/bitcoin_uk.ts b/src/qt/locale/bitcoin_uk.ts index ccdbd974b4..14d605a012 100644 --- a/src/qt/locale/bitcoin_uk.ts +++ b/src/qt/locale/bitcoin_uk.ts @@ -4198,7 +4198,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_ur_PK.ts b/src/qt/locale/bitcoin_ur_PK.ts index ab3088e506..5df6882c91 100644 --- a/src/qt/locale/bitcoin_ur_PK.ts +++ b/src/qt/locale/bitcoin_ur_PK.ts @@ -4183,7 +4183,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_vi.ts b/src/qt/locale/bitcoin_vi.ts index 0b8d5123e2..c4e0856831 100644 --- a/src/qt/locale/bitcoin_vi.ts +++ b/src/qt/locale/bitcoin_vi.ts @@ -4168,7 +4168,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_vi_VN.ts b/src/qt/locale/bitcoin_vi_VN.ts index 7739f04490..f095a57c86 100644 --- a/src/qt/locale/bitcoin_vi_VN.ts +++ b/src/qt/locale/bitcoin_vi_VN.ts @@ -4168,7 +4168,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 3bbdab3643..f57d8822e7 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -4180,7 +4180,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label 输入地址或标签进行搜索 diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index 9113a13713..7acf8f79ed 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -4168,7 +4168,7 @@ This label turns red, if the priority is smaller than "medium". - Enter address or label to search + Search by address or label 請輸入要搜尋的位址或標記 From 8f36b96f5e7ca7d6b65bc6554796b833ff00f42d Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 17 Apr 2021 21:58:09 -0400 Subject: [PATCH 120/280] Change transaction descriptions This commit changes the transaction descriptions to Mined - PoS Mined - PoS+RR Mined - Orphaned PoS Side Stake Received PoS+RR Side Stake Received PoS Side Stake Sent PoS+RR Side Stake Sent Mined - Superblock Mined - Unknown --- src/qt/transactiondesc.cpp | 18 +++++++++--------- src/qt/transactiontablemodel.cpp | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index c5bafe901b..c1f7a191ce 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -119,31 +119,31 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, unsigned int vo switch (gentype) { case MinedType::POS: - strHTML += tr("MINED - POS"); + strHTML += tr("Mined - PoS"); break; case MinedType::POR: - strHTML += tr("MINED - POR"); + strHTML += tr("Mined - PoS+RR"); break; case MinedType::ORPHANED: - strHTML += tr("MINED - ORPHANED"); + strHTML += tr("Mined - Orphaned"); break; case MinedType::POS_SIDE_STAKE_RCV: - strHTML += tr("POS SIDE STAKE RECEIVED"); + strHTML += tr("PoS Side Stake Received"); break; case MinedType::POR_SIDE_STAKE_RCV: - strHTML += tr("POR SIDE STAKE RECEIVED"); + strHTML += tr("PoS+RR Side Stake Received"); break; case MinedType::POS_SIDE_STAKE_SEND: - strHTML += tr("POS SIDE STAKE SENT"); + strHTML += tr("PoS Side Stake Sent"); break; case MinedType::POR_SIDE_STAKE_SEND: - strHTML += tr("POR SIDE STAKE SENT"); + strHTML += tr("PoS+RR Side Stake Sent"); break; case MinedType::SUPERBLOCK: - strHTML += tr("SUPERBLOCK"); + strHTML += tr("Mined - Superblock"); break; default: - strHTML += tr("MINED - UNKNOWN"); + strHTML += tr("Mined - Unknown"); break; } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index e5514651e7..b66956048c 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -404,23 +404,23 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const switch (wtx->status.generated_type) { case MinedType::POS: - return tr("MINED - POS"); + return tr("Mined - PoS"); case MinedType::POR: - return tr("MINED - POR"); + return tr("Mined - PoS+RR"); case MinedType::ORPHANED: - return tr("MINED - ORPHANED"); + return tr("Mined - Orphaned"); case MinedType::POS_SIDE_STAKE_RCV: - return tr("POS SIDE STAKE RECEIVED"); + return tr("PoS Side Stake Received"); case MinedType::POR_SIDE_STAKE_RCV: - return tr("POR SIDE STAKE RECEIVED"); + return tr("PoS+RR Side Stake Received"); case MinedType::POS_SIDE_STAKE_SEND: - return tr("POS SIDE STAKE SENT"); + return tr("PoS Side Stake Sent"); case MinedType::POR_SIDE_STAKE_SEND: - return tr("POR SIDE STAKE SENT"); + return tr("PoS+RR Side Stake Sent"); case MinedType::SUPERBLOCK: - return tr("MINED - SUPERBLOCK"); + return tr("Mined - Superblock"); default: - return tr("MINED - UNKNOWN"); + return tr("Mined - Unknown"); } } case TransactionRecord::BeaconAdvertisement: From c7f2fee05f5ef6e1811017c6af74e5d9336e62b9 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 17 Apr 2021 22:04:02 -0400 Subject: [PATCH 121/280] Change Proof of Research Difficulty to Difficulty --- src/qt/forms/rpcconsole.ui | 8 ++++---- src/qt/rpcconsole.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index 53d024f6c3..b61666dbdb 100755 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -7,7 +7,7 @@ 0 0 1000 - 720 + 944
@@ -48,7 +48,7 @@
- + N/A @@ -295,9 +295,9 @@ - + - Proof Of Research Difficulty + Difficulty diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index a2059d6a50..b79672a1f0 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -411,7 +411,7 @@ void RPCConsole::setClientModel(ClientModel *model) setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers()); ui->boostVersion->setText(model->formatBoostVersion()); - ui->porDiff->setText(model->getDifficulty()); + ui->diff->setText(model->getDifficulty()); //Setup autocomplete and attach it QStringList wordList; @@ -487,7 +487,7 @@ void RPCConsole::message(int category, const QString &message, bool html) void RPCConsole::setNumConnections(int count) { ui->numberOfConnections->setText(QString::number(count)); - if (clientModel) ui->porDiff->setText(clientModel->getDifficulty()); + if (clientModel) ui->diff->setText(clientModel->getDifficulty()); } @@ -500,7 +500,7 @@ void RPCConsole::setNumBlocks(int count, int countOfPeers) // If there is no current number available display N/A instead of 0, which can't ever be true ui->totalBlocks->setText(clientModel->getNumBlocksOfPeers() == 0 ? tr("N/A") : QString::number(clientModel->getNumBlocksOfPeers())); ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString()); - ui->porDiff->setText(clientModel->getDifficulty()); + ui->diff->setText(clientModel->getDifficulty()); } } From a67a0f89c71fb6960ccc6a059eb8ac205db052ac Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 17 Apr 2021 22:08:28 -0400 Subject: [PATCH 122/280] Change Beacon Rain Address to Beacon Address --- src/gridcoin/researcher.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index d895fc2f46..4f131115c6 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -659,12 +659,12 @@ AdvertiseBeaconResult GenerateBeaconKey(const Cpid& cpid) return BeaconError::MISSING_KEY; } - // Since the network-wide rain feature outputs GRC to beacon public key - // addresses, we describe this key as the participant's rain address to - // help identify transactions in the GUI: - // + // We label this key in the GUI to indicate to the user that it is a beacon key. + // The block number is also included because there are situations where there + // could be multiple beacon keys in the wallet due to expiration or forced re- + // advertisement. const std::string address_label = strprintf( - "Beacon Rain Address for CPID %s (at %" PRIu64 ")", + "Beacon Address for CPID %s (at %" PRIu64 ")", cpid.ToString(), static_cast(nBestHeight)); From 3182065922a45d9f2535fc4e113982a632bbc3a2 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 17 Apr 2021 22:17:57 -0400 Subject: [PATCH 123/280] Change Best Answer to Top Answer --- src/qt/votingdialog.cpp | 66 ++++++++++++++++++++++++----------------- src/qt/votingdialog.h | 8 ++--- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/qt/votingdialog.cpp b/src/qt/votingdialog.cpp index 52c1504e95..483d47cd4a 100644 --- a/src/qt/votingdialog.cpp +++ b/src/qt/votingdialog.cpp @@ -51,7 +51,7 @@ static int column_alignments[] = { Qt::AlignRight|Qt::AlignVCenter, // RowNumber Qt::AlignLeft|Qt::AlignVCenter, // Expiration Qt::AlignLeft|Qt::AlignVCenter, // Title - Qt::AlignLeft|Qt::AlignVCenter, // BestAnswer + Qt::AlignLeft|Qt::AlignVCenter, // TopAnswer Qt::AlignRight|Qt::AlignVCenter, // TotalParticipants Qt::AlignRight|Qt::AlignVCenter, // TotalShares Qt::AlignLeft|Qt::AlignVCenter, // ShareType @@ -65,7 +65,7 @@ VotingTableModel::VotingTableModel(void) << tr("#") << tr("Expiration") << tr("Title") - << tr("Best Answer") + << tr("Top Answer") << tr("# Voters") // Total Participants << tr("Total Shares") << tr("Share Type") @@ -115,8 +115,8 @@ QVariant VotingTableModel::data(const QModelIndex &index, int role) const return item->totalParticipants_; case TotalShares: return item->totalShares_; - case BestAnswer: - return item->bestAnswer_; + case TopAnswer: + return item->topAnswer_; default: ; } @@ -137,8 +137,8 @@ QVariant VotingTableModel::data(const QModelIndex &index, int role) const return item->totalParticipants_; case TotalShares: return item->totalShares_; - case BestAnswer: - return item->bestAnswer_; + case TopAnswer: + return item->topAnswer_; default: ; } @@ -162,8 +162,8 @@ QVariant VotingTableModel::data(const QModelIndex &index, int role) const case TotalSharesRole: return item->totalShares_; - case BestAnswerRole: - return item->bestAnswer_; + case TopAnswerRole: + return item->topAnswer_; case Qt::TextAlignmentRole: return column_alignments[index.column()]; @@ -202,8 +202,8 @@ QVariant VotingTableModel::headerData(int section, Qt::Orientation orientation, return tr("Total Participants."); case TotalShares: return tr("Total Shares."); - case BestAnswer: - return tr("Best Answer."); + case TopAnswer: + return tr("Top Answer."); } } } @@ -269,7 +269,7 @@ VotingItem* BuildPollItem(const PollRegistry::Sequence::Iterator& iter) } if (!result->m_votes.empty()) { - item->bestAnswer_ = QString::fromStdString(result->WinnerLabel()).replace("_"," "); + item->topAnswer_ = QString::fromStdString(result->WinnerLabel()).replace("_"," "); } return item; @@ -424,7 +424,13 @@ VotingDialog::VotingDialog(QWidget *parent) tableView_->setModel(proxyModel_); tableView_->setFont(QFont("Arial", 10)); tableView_->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - tableView_->horizontalHeader()->setMinimumWidth(VOTINGDIALOG_WIDTH_RowNumber + VOTINGDIALOG_WIDTH_Title + VOTINGDIALOG_WIDTH_Expiration + VOTINGDIALOG_WIDTH_ShareType + VOTINGDIALOG_WIDTH_TotalParticipants + VOTINGDIALOG_WIDTH_TotalShares + VOTINGDIALOG_WIDTH_BestAnswer); + tableView_->horizontalHeader()->setMinimumWidth(VOTINGDIALOG_WIDTH_RowNumber + + VOTINGDIALOG_WIDTH_Title + + VOTINGDIALOG_WIDTH_Expiration + + VOTINGDIALOG_WIDTH_ShareType + + VOTINGDIALOG_WIDTH_TotalParticipants + + VOTINGDIALOG_WIDTH_TotalShares + + VOTINGDIALOG_WIDTH_TopAnswer); tableView_->verticalHeader()->setDefaultSectionSize(40); groupboxvlayout->addWidget(tableView_); @@ -443,7 +449,8 @@ VotingDialog::VotingDialog(QWidget *parent) voteDialog_ = new VotingVoteDialog(this); pollDialog_ = new NewPollDialog(this); - loadingIndicator->setText(tr("Press reload to load polls... This can take several minutes, and the wallet may not respond until finished.")); + loadingIndicator->setText(tr("Press reload to load polls... This can take several minutes, and the wallet may not " + "respond until finished.")); tableView_->hide(); loadingIndicator->show(); @@ -468,7 +475,8 @@ void VotingDialog::loadPolls(bool history) bool isRunning = watcher.property("running").toBool(); if (tableModel_&& !isRunning) { - loadingIndicator->setText(tr("Recalculating voting weights... This can take several minutes, and the wallet may not respond until finished.")); + loadingIndicator->setText(tr("Recalculating voting weights... This can take several minutes, and the wallet may not " + "respond until finished.")); tableView_->hide(); loadingIndicator->show(); @@ -537,11 +545,15 @@ void VotingDialog::tableColResize(void) tableView_->setColumnWidth(VotingTableModel::TotalParticipants, VOTINGDIALOG_WIDTH_TotalParticipants); tableView_->setColumnWidth(VotingTableModel::TotalShares, VOTINGDIALOG_WIDTH_TotalShares); - int fixedColWidth = VOTINGDIALOG_WIDTH_RowNumber + VOTINGDIALOG_WIDTH_Expiration + VOTINGDIALOG_WIDTH_ShareType + VOTINGDIALOG_WIDTH_TotalParticipants + VOTINGDIALOG_WIDTH_TotalShares; + int fixedColWidth = VOTINGDIALOG_WIDTH_RowNumber + + VOTINGDIALOG_WIDTH_Expiration + + VOTINGDIALOG_WIDTH_ShareType + + VOTINGDIALOG_WIDTH_TotalParticipants + + VOTINGDIALOG_WIDTH_TotalShares; int dynamicWidth = tableView_->horizontalHeader()->width() - fixedColWidth; int nColumns = 2; // 2 dynamic columns - int columns[] = {VotingTableModel::Title,VotingTableModel::BestAnswer}; + int columns[] = {VotingTableModel::Title,VotingTableModel::TopAnswer}; int remainingWidth = dynamicWidth % nColumns; for(int cNum = 0; cNum < nColumns; cNum++) { if(remainingWidth > 0) @@ -728,10 +740,10 @@ VotingChartDialog::VotingChartDialog(QWidget *parent) url_->setOpenExternalLinks(true); glayout->addWidget(url_, 1, 1); - QLabel *bestAnswer = new QLabel(tr("Best Answer: ")); - bestAnswer->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - bestAnswer->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(bestAnswer, 3, 0); + QLabel *topAnswer = new QLabel(tr("Top Answer: ")); + topAnswer->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + topAnswer->setTextInteractionFlags(Qt::TextSelectableByMouse); + glayout->addWidget(topAnswer, 3, 0); answer_ = new QLabel(); answer_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); @@ -786,8 +798,8 @@ void VotingChartDialog::resetData(const VotingItem *item) question_->setText(item->question_); url_->setText("
url_+"\">"+item->url_+""); - answer_->setText(item->bestAnswer_); - answer_->setVisible(!item->bestAnswer_.isEmpty()); + answer_->setText(item->topAnswer_); + answer_->setVisible(!item->topAnswer_.isEmpty()); answerModel_->setRowCount(item->vectorOfAnswers_.size()); for (size_t y = 0; y < item->vectorOfAnswers_.size(); y++) @@ -876,10 +888,10 @@ VotingVoteDialog::VotingVoteDialog(QWidget *parent) responseType_->setTextInteractionFlags(Qt::TextSelectableByMouse); glayout->addWidget(responseType_, 3, 1); - QLabel *bestAnswer = new QLabel(tr("Best Answer: ")); - bestAnswer->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - bestAnswer->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(bestAnswer, 4, 0); + QLabel *topAnswer = new QLabel(tr("Top Answer: ")); + topAnswer->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + topAnswer->setTextInteractionFlags(Qt::TextSelectableByMouse); + glayout->addWidget(topAnswer, 4, 0); answer_ = new QLabel(); answer_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); @@ -924,7 +936,7 @@ void VotingVoteDialog::resetData(const VotingItem *item) question_->setText(item->question_); url_->setText("url_+"\">"+item->url_+""); responseType_->setText(item->responseType_); - answer_->setText(item->bestAnswer_); + answer_->setText(item->topAnswer_); pollTxid_ = item->pollTxid_; for (const auto& choice : item->vectorOfAnswers_) { diff --git a/src/qt/votingdialog.h b/src/qt/votingdialog.h index 0de6b64835..7299e7a1ea 100644 --- a/src/qt/votingdialog.h +++ b/src/qt/votingdialog.h @@ -48,7 +48,7 @@ class WalletModel; #define VOTINGDIALOG_WIDTH_ShareType 80 #define VOTINGDIALOG_WIDTH_TotalParticipants 80 #define VOTINGDIALOG_WIDTH_TotalShares 100 -#define VOTINGDIALOG_WIDTH_BestAnswer 80 +#define VOTINGDIALOG_WIDTH_TopAnswer 80 namespace polling { // TODO: Legacy struct moved here until we redesign the voting GUI. @@ -79,7 +79,7 @@ class VotingItem { unsigned int totalParticipants_; unsigned int totalShares_; QString url_; - QString bestAnswer_; + QString topAnswer_; }; // VotingTableModel @@ -97,7 +97,7 @@ class VotingTableModel RowNumber = 0, Expiration = 1, Title = 2, - BestAnswer = 3, + TopAnswer = 3, TotalParticipants = 4, TotalShares = 5, ShareType = 6, @@ -107,7 +107,7 @@ class VotingTableModel RowNumberRole = Qt::UserRole, ExpirationRole, TitleRole, - BestAnswerRole, + TopAnswerRole, TotalParticipantsRole, TotalSharesRole, ShareTypeRole, From 9d99e5d06b801bcab498162ab992e581ae9b51d3 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Thu, 13 May 2021 10:43:27 -0500 Subject: [PATCH 124/280] Refresh GUI address book page design This updates the GUI address book page to match the proposed redesign: - Add a header bar with address/label filter input - Limit the max content width for large window sizes - Update colors, layout, sizing, and spacing --- gridcoinresearch.pro | 3 + src/Makefile.qt.include | 4 + src/qt/bitcoingui.cpp | 27 ++--- src/qt/bitcoingui.h | 5 +- src/qt/favoritespage.cpp | 64 ++++++++++++ src/qt/favoritespage.h | 49 +++++++++ src/qt/forms/favoritespage.ui | 180 ++++++++++++++++++++++++++++++++++ src/qt/transactionview.cpp | 1 + 8 files changed, 312 insertions(+), 21 deletions(-) create mode 100644 src/qt/favoritespage.cpp create mode 100644 src/qt/favoritespage.h create mode 100644 src/qt/forms/favoritespage.ui diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 03331f5158..49aec3b393 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -192,6 +192,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/coincontroltreewidget.h \ src/qt/receivecoinspage.h \ src/qt/sendcoinsdialog.h \ + src/qt/favoritespage.h \ src/qt/addressbookpage.h \ src/qt/signverifymessagedialog.h \ src/qt/aboutdialog.h \ @@ -296,6 +297,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/optionsdialog.cpp \ src/qt/receivecoinspage.cpp \ src/qt/sendcoinsdialog.cpp \ + src/qt/favoritespage.cpp \ src/qt/coincontroldialog.cpp \ src/qt/coincontroltreewidget.cpp \ src/qt/addressbookpage.cpp \ @@ -395,6 +397,7 @@ FORMS += \ src/qt/forms/researcherwizardsummarypage.ui \ src/qt/forms/receivecoinspage.ui \ src/qt/forms/sendcoinsdialog.ui \ + src/qt/forms/favoritespage.ui \ src/qt/forms/addressbookpage.ui \ src/qt/forms/signverifymessagedialog.ui \ src/qt/forms/aboutdialog.ui \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index bcd333a036..93ae56198d 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -80,6 +80,7 @@ QT_FORMS_UI = \ qt/forms/consolidateunspentwizardselectinputspage.ui \ qt/forms/consolidateunspentwizardsendpage.ui \ qt/forms/diagnosticsdialog.ui \ + qt/forms/favoritespage.ui \ qt/forms/optionsdialog.ui \ qt/forms/rpcconsole.ui \ qt/forms/signverifymessagedialog.ui \ @@ -126,6 +127,7 @@ QT_MOC_CPP = \ qt/moc_csvmodelwriter.cpp \ qt/moc_diagnosticsdialog.cpp \ qt/moc_editaddressdialog.cpp \ + qt/moc_favoritespage.cpp \ qt/moc_guiutil.cpp \ qt/moc_intro.cpp \ qt/moc_macdockiconhandler.cpp \ @@ -204,6 +206,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/decoration.h \ qt/diagnosticsdialog.h \ qt/editaddressdialog.h \ + qt/favoritespage.h \ qt/guiconstants.h \ qt/guiutil.h \ qt/intro.h \ @@ -273,6 +276,7 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/decoration.cpp \ qt/diagnosticsdialog.cpp \ qt/editaddressdialog.cpp \ + qt/favoritespage.cpp \ qt/guiutil.cpp \ qt/intro.cpp \ qt/monitoreddatamapper.cpp \ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6ff58bf6df..4acab47a02 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -12,11 +12,11 @@ #include "qt/decoration.h" #include "bitcoingui.h" #include "transactiontablemodel.h" -#include "addressbookpage.h" #include "diagnosticsdialog.h" #include "receivecoinspage.h" #include "sendcoinsdialog.h" +#include "favoritespage.h" #include "signverifymessagedialog.h" #include "optionsdialog.h" #include "aboutdialog.h" @@ -182,29 +182,20 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create tabs overviewPage = new OverviewPage(); - - transactionView = new TransactionView(this); - - addressBookPage = new QWidget(this); - addressBook = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); - QVBoxLayout *addressBookPageLayout = new QVBoxLayout(); - addressBookPageLayout->addWidget(addressBook); - addressBookPage->setLayout(addressBookPageLayout); - - receiveCoinsPage = new ReceiveCoinsPage(this); - sendCoinsPage = new SendCoinsDialog(this); - + receiveCoinsPage = new ReceiveCoinsPage(this); + transactionView = new TransactionView(this); + addressBookPage = new FavoritesPage(this); votingPage = new VotingDialog(this); signVerifyMessageDialog = new SignVerifyMessageDialog(this); centralWidget = new QStackedWidget(this); centralWidget->addWidget(overviewPage); + centralWidget->addWidget(sendCoinsPage); + centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(transactionView); centralWidget->addWidget(addressBookPage); - centralWidget->addWidget(receiveCoinsPage); - centralWidget->addWidget(sendCoinsPage); centralWidget->addWidget(votingPage); setCentralWidget(centralWidget); @@ -757,7 +748,7 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); rpcConsole->setClientModel(clientModel); - addressBook->setOptionsModel(clientModel->getOptionsModel()); + addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); } } @@ -778,7 +769,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) transactionView->setModel(walletModel); overviewPage->setWalletModel(walletModel); - addressBook->setModel(walletModel->getAddressTableModel()); + addressBookPage->setAddressTableModel(walletModel->getAddressTableModel()); receiveCoinsPage->setAddressTableModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); votingPage->setModel(walletModel); @@ -1287,7 +1278,7 @@ void BitcoinGUI::gotoAddressBookPage() exportAction->setEnabled(true); disconnect(exportAction, SIGNAL(triggered()), 0, 0); - connect(exportAction, SIGNAL(triggered()), addressBook, SLOT(exportClicked())); + connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked())); } void BitcoinGUI::gotoReceiveCoinsPage() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 41771acf41..f7520e0692 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -18,7 +18,7 @@ class WalletModel; class ResearcherModel; class TransactionView; class OverviewPage; -class AddressBookPage; +class FavoritesPage; class ReceiveCoinsPage; class SendCoinsDialog; class VotingDialog; @@ -79,7 +79,7 @@ class BitcoinGUI : public QMainWindow QStackedWidget *centralWidget; OverviewPage *overviewPage; - QWidget *addressBookPage; + FavoritesPage *addressBookPage; ReceiveCoinsPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; TransactionView *transactionView; @@ -135,7 +135,6 @@ class BitcoinGUI : public QMainWindow QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; Notificator *notificator; - AddressBookPage *addressBook; RPCConsole *rpcConsole; DiagnosticsDialog *diagnosticsDialog; diff --git a/src/qt/favoritespage.cpp b/src/qt/favoritespage.cpp new file mode 100644 index 0000000000..c4298e63c9 --- /dev/null +++ b/src/qt/favoritespage.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/addressbookpage.h" +#include "qt/decoration.h" +#include "qt/forms/ui_favoritespage.h" +#include "qt/optionsmodel.h" +#include "qt/favoritespage.h" + +#include +#include + +FavoritesPage::FavoritesPage(QWidget* parent) + : QWidget(parent) + , ui(new Ui::FavoritesPage) + , addressBookPage(new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab)) + , filterLineEditIconAction(new QAction()) +{ + ui->setupUi(this); + ui->contentFrameVerticalLayout->addWidget(addressBookPage); + ui->filterLineEdit->addAction(filterLineEditIconAction, QLineEdit::LeadingPosition); + + GRC::ScaleFontPointSize(ui->headerTitleLabel, 15); + + connect( + ui->filterLineEdit, &QLineEdit::textChanged, + addressBookPage, &AddressBookPage::changeFilter); + connect( + addressBookPage, &AddressBookPage::verifyMessage, + [this](const QString& address) { emit verifyMessage(address); }); +} + +FavoritesPage::~FavoritesPage() +{ + delete ui; + delete addressBookPage; + delete filterLineEditIconAction; +} + +void FavoritesPage::setAddressTableModel(AddressTableModel* model) +{ + addressBookPage->setModel(model); +} + +void FavoritesPage::setOptionsModel(OptionsModel* model) +{ + addressBookPage->setOptionsModel(model); + + if (model) { + connect(model, SIGNAL(walletStylesheetChanged(QString)), this, SLOT(updateIcons(QString))); + updateIcons(model->getCurrentStyle()); + } +} + +void FavoritesPage::exportClicked() +{ + addressBookPage->exportClicked(); +} + +void FavoritesPage::updateIcons(const QString& theme) +{ + filterLineEditIconAction->setIcon(QIcon(":/icons/" + theme + "_search")); +} diff --git a/src/qt/favoritespage.h b/src/qt/favoritespage.h new file mode 100644 index 0000000000..6a2239f524 --- /dev/null +++ b/src/qt/favoritespage.h @@ -0,0 +1,49 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef FAVORITESPAGE_H +#define FAVORITESPAGE_H + +#include + +namespace Ui { + class FavoritesPage; +} + +class AddressBookPage; +class AddressTableModel; +class OptionsModel; + +QT_BEGIN_NAMESPACE +class QAction; +class QString; +QT_END_NAMESPACE + +class FavoritesPage : public QWidget +{ + Q_OBJECT + +public: + explicit FavoritesPage(QWidget* parent = nullptr); + ~FavoritesPage(); + + void setAddressTableModel(AddressTableModel* model); + void setOptionsModel(OptionsModel* model); + +private: + Ui::FavoritesPage* ui; + AddressBookPage* addressBookPage; + QAction* filterLineEditIconAction; + +signals: + void verifyMessage(QString address); + +public slots: + void exportClicked(); + +private slots: + void updateIcons(const QString& theme); +}; + +#endif // FAVORITESPAGE_H diff --git a/src/qt/forms/favoritespage.ui b/src/qt/forms/favoritespage.ui new file mode 100644 index 0000000000..1d478508f6 --- /dev/null +++ b/src/qt/forms/favoritespage.ui @@ -0,0 +1,180 @@ + + + FavoritesPage + + + + 0 + 0 + 899 + 456 + + + + Favorites + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Favorites + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Search by label or address + + + true + + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + 1 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 9 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 1fae11f68b..c44570c19b 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -112,6 +112,7 @@ TransactionView::TransactionView(QWidget *parent) QVBoxLayout *tableViewLayout = new QVBoxLayout(); tableViewLayout->setContentsMargins(9, 9, 9, 9); QFrame *tableViewFrame = new QFrame(this); + tableViewFrame->setObjectName("historyTableFrame"); tableViewFrame->setLayout(new QVBoxLayout()); tableViewFrame->layout()->setContentsMargins(0, 0, 0, 0); tableViewFrame->layout()->addWidget(view); From 16dca010013b1ad6ab9e322e3e46482bb34230d4 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Thu, 20 May 2021 22:20:36 +0300 Subject: [PATCH 125/280] wallet: simplify nTimeSmart calculation --- src/wallet/wallet.cpp | 49 +++++++------------------------------------ 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 936df1d14f..8b62566e6a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -480,9 +480,11 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, CWalletDB* pwalletdb) LOCK(cs_wallet); // Inserts only if not already there, returns tx inserted or tx found pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); - CWalletTx& wtx = (*ret.first).second; + CWalletTx& wtx = ret.first->second; wtx.BindWallet(this); bool fInsertedNew = ret.second; + bool fUpdated = false; + if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); @@ -494,53 +496,16 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, CWalletDB* pwalletdb) auto mapItem = mapBlockIndex.find(wtxIn.hashBlock); if (mapItem != mapBlockIndex.end()) { - unsigned int latestNow = wtx.nTimeReceived; - unsigned int latestEntry = 0; - { - // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future - int64_t latestTolerated = latestNow + 300; - std::list acentries; - TxItems txOrdered = OrderedTxItems(acentries); - for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx == &wtx) - continue; - CAccountingEntry *const pacentry = (*it).second.second; - int64_t nSmartTime; - if (pwtx) - { - nSmartTime = pwtx->nTimeSmart; - if (!nSmartTime) - nSmartTime = pwtx->nTimeReceived; - } - else - nSmartTime = pacentry->nTime; - if (nSmartTime <= latestTolerated) - { - latestEntry = nSmartTime; - if (nSmartTime > latestNow) - latestNow = nSmartTime; - break; - } - } - } - - unsigned int& blocktime = mapItem->second->nTime; - wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); + wtx.nTimeSmart = mapItem->second->nTime; } else { LogPrint(BCLog::LogFlags::VERBOSE, "AddToWallet() : found %s in block %s not in index", - wtxIn.GetHash().ToString().substr(0,10), + hash.ToString().substr(0,10), wtxIn.hashBlock.ToString()); } } - } - - bool fUpdated = false; - if (!fInsertedNew) - { + } else { // Merge if (!wtxIn.hashBlock.IsNull() && wtxIn.hashBlock != wtx.hashBlock) { @@ -594,7 +559,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, CWalletDB* pwalletdb) std::string strCmd = GetArg("-walletnotify", ""); if (!strCmd.empty()) { - boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::replace_all(strCmd, "%s", hash.GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } From 0bf596c6b29cef8f35f3ebddc96cc5934808b346 Mon Sep 17 00:00:00 2001 From: Raphael Husistein Date: Tue, 25 May 2021 16:41:51 +0200 Subject: [PATCH 126/280] add issue template for bug reports and feature requests --- .github/ISSUE_TEMPLATE/bug_report.md | 40 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 30 +++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..05b804aafb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: 🐜 Bug Report +about: Create a report to help us improve +title: '[bug] ' +--- + + + + + + + +# Bug Report + +**Current behavior:** + + +**Expected behavior** + + +**Steps to reproduce:** + + +**Screenshots** + + +**Gridcoin version** + + +**Machine specs** +- OS: +- CPU: +- RAM: +- Disk size: +- Disk Type (HD/SDD): + +**Extra information** + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..6679380d33 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,30 @@ +--- +name: 🚀 Feature Request +about: Suggest an idea for this project +title: '[feat] ' +--- + + + + + + + +# Feature Request + +**Describe the Feature Request** + + +**Describe Preferred Solution** + + +**Describe Alternatives** + + +**Related Code** + + +**Additional Context** + \ No newline at end of file From 05b444ccb1fc5b6b55784bd90a08824ae5d24071 Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Wed, 26 May 2021 10:45:45 +0000 Subject: [PATCH 127/280] Apply suggestions from nathanielcwm Co-authored-by: Nathaniel Chin <22572406+nathanielcwm@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 6 ++++-- .github/ISSUE_TEMPLATE/feature_request.md | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 05b804aafb..d6e3719a9f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,6 +2,7 @@ name: 🐜 Bug Report about: Create a report to help us improve title: '[bug] ' +labels: bug --- @@ -27,7 +28,8 @@ title: '[bug] ' **Gridcoin version** - + **Machine specs** - OS: @@ -37,4 +39,4 @@ title: '[bug] ' - Disk Type (HD/SDD): **Extra information** - \ No newline at end of file + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 6679380d33..3fed9cc59a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,6 +2,7 @@ name: 🚀 Feature Request about: Suggest an idea for this project title: '[feat] ' +labels: enhancement --- @@ -27,4 +28,4 @@ title: '[feat] ' **Additional Context** - \ No newline at end of file + From fb43e8f9a59a62dfd37ba1d658582ffb09122169 Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 26 May 2021 18:48:21 -0400 Subject: [PATCH 128/280] Update http to https where possible Each of these links has been checked and verified --- CHANGELOG.md | 4 +-- build-aux/m4/ax_cxx_compile_stdcxx.m4 | 2 +- contrib/devtools/clang-format-diff.py | 2 +- contrib/macdeploy/LICENSE | 2 +- doc/Doxyfile.in | 36 +++++++++++++-------------- doc/README.md | 4 +-- doc/assets-attribution.md | 4 +-- doc/build-macos.md | 2 +- doc/gitian-building.md | 2 +- doc/readme-qt.rst | 8 +++--- src/crypto/ctaes/README.md | 2 +- src/crypto/ctaes/ctaes.c | 2 +- src/leveldb/doc/benchmark.html | 4 +-- src/leveldb/doc/impl.md | 2 +- src/leveldb/port/atomic_pointer.h | 4 +-- src/leveldb/port/port_posix.h | 2 +- src/leveldb/util/env_win.cc | 6 ++--- src/qt/notificator.cpp | 2 +- src/rpc/protocol.cpp | 2 +- src/threadsafety.h | 2 +- src/tinyformat.h | 2 +- src/util.cpp | 4 +-- src/wallet/rpcdump.cpp | 2 +- 23 files changed, 51 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3233e7960d..27a415468f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Change Log All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). +The format is based on [Keep a Changelog](https://keepachangelog.com/) +and this project adheres to [Semantic Versioning](https://semver.org/). ## [5.3.1.0] 2021-04-04, leisure ### Added diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 index 43087b2e68..a45948e23b 100644 --- a/build-aux/m4/ax_cxx_compile_stdcxx.m4 +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -415,7 +415,7 @@ namespace cxx11 } - // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // https://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py index 98eee67f43..0fead5fa74 100755 --- a/contrib/devtools/clang-format-diff.py +++ b/contrib/devtools/clang-format-diff.py @@ -21,7 +21,7 @@ # # University of Illinois at Urbana-Champaign # -# http://llvm.org +# https://llvm.org # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal with diff --git a/contrib/macdeploy/LICENSE b/contrib/macdeploy/LICENSE index 94a9ed024d..771760c87c 100644 --- a/contrib/macdeploy/LICENSE +++ b/contrib/macdeploy/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 7d18ffd2b5..04c46bd72f 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -295,7 +295,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -337,7 +337,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -708,7 +708,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -1181,7 +1181,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1361,7 +1361,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1369,7 +1369,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1378,7 +1378,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1386,7 +1386,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1394,7 +1394,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1499,7 +1499,7 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1511,7 +1511,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# https://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1526,11 +1526,11 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://www.mathjax.org/mathjax +MATHJAX_RELPATH = https://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example @@ -1541,7 +1541,7 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: https://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1588,7 +1588,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1601,7 +1601,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1788,7 +1788,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. diff --git a/doc/README.md b/doc/README.md index 930eb3bbbd..3dc1bfe585 100644 --- a/doc/README.md +++ b/doc/README.md @@ -12,10 +12,10 @@ To download Gridcoin, visit [gridcoin.us](https://gridcoin.us). ### Need Help? -* See the documentation at the [Gridcoin Wiki](http://wiki.gridcoin.us/Main_Page) +* See the documentation at the [Gridcoin Wiki](https://wiki.gridcoin.us/Main_Page) for help and more information. * A lot of features core features are based on Bitcoin and have been documented on the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) -* Ask for help or discuss on [#gridcoin](http://webchat.freenode.net?channels=gridcoin) on Freenode. +* Ask for help or discuss on [#gridcoin](https://webchat.freenode.net?channels=gridcoin) on Freenode. * Ask for help or discuss on the [Cryptocurrencytalk](https://cryptocurrencytalk.com/forum/464-gridcoin-grc/) forums * You can also join us on [Slack](https://grcinvite.herokuapp.com/) with the invite Token GRCsquad diff --git a/doc/assets-attribution.md b/doc/assets-attribution.md index 878a7f4d30..a1f3506cf9 100644 --- a/doc/assets-attribution.md +++ b/doc/assets-attribution.md @@ -25,7 +25,7 @@ Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, src/qt/res/icons/add.png, src/qt/res/icons/edit.png, src/qt/res/icons/remove.png (edited) -Designer: http://www.everaldo.com +Designer: https://www.everaldo.com Icon Pack: Crystal SVG License: LGPL @@ -33,6 +33,6 @@ Icon: scripts/img/reload.xcf (modified),src/qt/res/movies/update_spinner.mng Icon Pack: Kids Designer: Everaldo (Everaldo Coelho) License: GNU/GPL -Site: http://findicons.com/icon/17102/reload?id=17102 +Site: https://findicons.com/icon/17102/reload?id=17102 Gridcoin Logo: CryptoCoinTalk Community diff --git a/doc/build-macos.md b/doc/build-macos.md index ca711356dc..f5f843a3f0 100644 --- a/doc/build-macos.md +++ b/doc/build-macos.md @@ -88,7 +88,7 @@ brew install berkeley-db4 make deploy ``` -5. Testnet participation info is found at [Using Testnet](http://wiki.gridcoin.us/OS_X_Guide#Using_Testnet). +5. Testnet participation info is found at [Using Testnet](https://wiki.gridcoin.us/OS_X_Guide#Using_Testnet). To open the app in testnet mode: ```shell diff --git a/doc/gitian-building.md b/doc/gitian-building.md index 9f9afaf04f..a83d7cbdaa 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -227,7 +227,7 @@ Connecting to the VM After the VM has booted you can connect to it using SSH, and files can be copied from and to the VM using a SFTP utility. Connect to `localhost`, port `22222` (or the port configured when installing the VM). -On Windows you can use [putty](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and [WinSCP](http://winscp.net/eng/index.php). +On Windows you can use [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and [WinSCP](https://winscp.net/eng/index.php). For example, to connect as `root` from a Linux command prompt use diff --git a/doc/readme-qt.rst b/doc/readme-qt.rst index 338ee98d25..216968a1b5 100644 --- a/doc/readme-qt.rst +++ b/doc/readme-qt.rst @@ -40,7 +40,7 @@ Windows build instructions: - Open the .pro file in QT creator and build as normal (ctrl-B) -.. _`QT Windows SDK`: http://qt-project.org/downloads +.. _`QT Windows SDK`: https://qt-project.org/downloads MacOS @@ -59,8 +59,8 @@ MacOS - Open the .pro file in Qt Creator and build as normal (cmd-B) -.. _`Qt Mac OS X SDK`: http://qt-project.org/downloads -.. _`MacPorts`: http://www.macports.org/install.php +.. _`Qt Mac OS X SDK`: https://qt-project.org/downloads +.. _`MacPorts`: https://www.macports.org/install.php Alternatively ------------- @@ -113,7 +113,7 @@ Generation of QR codes ---------------------- libqrencode may be used to generate QRCode images for payment requests. -It can be downloaded from http://fukuchi.org/works/qrencode/index.html.en, or installed via your package manager. Pass the USE_QRCODE +It can be downloaded from https://fukuchi.org/works/qrencode/index.html.en, or installed via your package manager. Pass the USE_QRCODE flag to qmake to control this: +--------------+--------------------------------------------------------------------------+ diff --git a/src/crypto/ctaes/README.md b/src/crypto/ctaes/README.md index 0e7fe17751..0b0173dbdf 100644 --- a/src/crypto/ctaes/README.md +++ b/src/crypto/ctaes/README.md @@ -38,4 +38,4 @@ Benchmark: Review ------ -Results of a formal review of the code can be found in http://bitcoin.sipa.be/ctaes/review.zip +Results of a formal review of the code can be found in https://bitcoin.sipa.be/ctaes/review.zip diff --git a/src/crypto/ctaes/ctaes.c b/src/crypto/ctaes/ctaes.c index 55962bf252..9a9487abbd 100644 --- a/src/crypto/ctaes/ctaes.c +++ b/src/crypto/ctaes/ctaes.c @@ -7,7 +7,7 @@ /* Constant time, unoptimized, concise, plain C, AES implementation * Based On: * Emilia Kasper and Peter Schwabe, Faster and Timing-Attack Resistant AES-GCM - * http://www.iacr.org/archive/ches2009/57470001/57470001.pdf + * https://www.iacr.org/archive/ches2009/57470001/57470001.pdf * But using 8 16-bit integers representing a single AES state rather than 8 128-bit * integers representing 8 AES states. */ diff --git a/src/leveldb/doc/benchmark.html b/src/leveldb/doc/benchmark.html index c4639772c1..037115339c 100644 --- a/src/leveldb/doc/benchmark.html +++ b/src/leveldb/doc/benchmark.html @@ -83,7 +83,7 @@

LevelDB Benchmarks

Google, July 2011


-

In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against SQLite3 (version 3.7.6.3) and Kyoto Cabinet's (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.

+

In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against SQLite3 (version 3.7.6.3) and Kyoto Cabinet's (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.

Benchmarks were all performed on a six-core Intel(R) Xeon(R) CPU X5650 @ 2.67GHz, with 12288 KB of total L3 cache and 12 GB of DDR3 RAM at 1333 MHz. (Note that LevelDB uses at most two CPUs since the benchmarks are single threaded: one to run the benchmark, and one for background compactions.) We ran the benchmarks on two machines (with identical processors), one with an Ext3 file system and one with an Ext4 file system. The machine with the Ext3 file system has a SATA Hitachi HDS721050CLA362 hard drive. The machine with the Ext4 file system has a SATA Samsung HD502HJ hard drive. Both hard drives spin at 7200 RPM and have hard drive write-caching enabled (using `hdparm -W 1 [device]`). The numbers reported below are the median of three measurements.

@@ -451,7 +451,7 @@

Random Reads

of the working set to fit in memory.

Note about Ext4 Filesystems

-

The preceding numbers are for an ext3 file system. Synchronous writes are much slower under ext4 (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of fsync / msync calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues fsync calls when switching to a new file.

+

The preceding numbers are for an ext3 file system. Synchronous writes are much slower under ext4 (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of fsync / msync calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues fsync calls when switching to a new file.

Acknowledgements

Jeff Dean and Sanjay Ghemawat wrote LevelDB. Kevin Tseng wrote and compiled these benchmarks. Mikio Hirabayashi, Scott Hess, and Gabor Cselle provided help and advice.

diff --git a/src/leveldb/doc/impl.md b/src/leveldb/doc/impl.md index 4b13f2a6ba..6c15152255 100644 --- a/src/leveldb/doc/impl.md +++ b/src/leveldb/doc/impl.md @@ -1,7 +1,7 @@ ## Files The implementation of leveldb is similar in spirit to the representation of a -single [Bigtable tablet (section 5.3)](http://research.google.com/archive/bigtable.html). +single [Bigtable tablet (section 5.3)](https://research.google.com/archive/bigtable.html). However the organization of the files that make up the representation is somewhat different and is explained below. diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h index d79a02230d..1406b59488 100644 --- a/src/leveldb/port/atomic_pointer.h +++ b/src/leveldb/port/atomic_pointer.h @@ -88,7 +88,7 @@ inline void MemoryBarrier() { #elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) inline void MemoryBarrier() { // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + // this idiom. Also see https://en.wikipedia.org/wiki/Memory_ordering. __asm__ __volatile__("" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER @@ -97,7 +97,7 @@ inline void MemoryBarrier() { #elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) inline void MemoryBarrier() { // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + // this idiom. Also see https://en.wikipedia.org/wiki/Memory_ordering. asm volatile("" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h index d85fa5d63f..ad17fb47ae 100644 --- a/src/leveldb/port/port_posix.h +++ b/src/leveldb/port/port_posix.h @@ -31,7 +31,7 @@ #elif defined(OS_ANDROID) // Due to a bug in the NDK x86 definition, // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. - // See http://code.google.com/p/android/issues/detail?id=39824 + // See https://code.google.com/p/android/issues/detail?id=39824 #include #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) #else diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc index 830332abe9..e31afd8e7d 100644 --- a/src/leveldb/util/env_win.cc +++ b/src/leveldb/util/env_win.cc @@ -1,8 +1,8 @@ // This file contains source that originates from: -// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h -// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc +// https://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h +// https://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc // Those files don't have any explicit license headers but the -// project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' +// project (https://code.google.com/p/leveldbwin/) lists the 'New BSD License' // as the license. #if defined(LEVELDB_PLATFORM_WINDOWS) #include diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index dbd82f909e..50c9a194d8 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -71,7 +71,7 @@ Notificator::~Notificator() #ifdef USE_DBUS -// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html +// Loosely based on https://www.qtcentre.org/archive/index.php/t-25879.html class FreedesktopImage { public: diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index adfb2e89a8..7bad395f58 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -219,7 +219,7 @@ int ReadHTTPMessage(std::basic_istream& stream, std::map Date: Wed, 26 May 2021 19:05:01 -0400 Subject: [PATCH 129/280] Fix Typos from lint --- src/gridcoin/beacon.h | 2 +- src/gridcoin/scraper/scraper.cpp | 4 ++-- src/qt/consolidateunspentwizardselectinputspage.cpp | 2 +- src/qt/decoration.h | 4 ++-- src/rpc/net.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gridcoin/beacon.h b/src/gridcoin/beacon.h index 1117a38ad8..ca95142d86 100644 --- a/src/gridcoin/beacon.h +++ b/src/gridcoin/beacon.h @@ -525,7 +525,7 @@ class BeaconRegistry : public IContractHandler //! //! \brief The type that associates historical beacons with a SHA256 hash. This is done - //! with smart pointers to save memeory. Note that most of the time this is the hash of + //! with smart pointers to save memory. Note that most of the time this is the hash of //! the beacon contract, but in the case of beacon activations in the superblock, this //! is a hash of the superblock hash and the pending beacon that is being activated's hash //! to make the records unique. diff --git a/src/gridcoin/scraper/scraper.cpp b/src/gridcoin/scraper/scraper.cpp index 0487b0866e..bf24ff7f17 100755 --- a/src/gridcoin/scraper/scraper.cpp +++ b/src/gridcoin/scraper/scraper.cpp @@ -1126,7 +1126,7 @@ void Scraper(bool bSingleShot) else _log(logattribute::INFO, "Scraper", "Stored Beacon List"); - // If team filtering is set by policy then pull down and retrieve team ID's as needed. This loads the TeamIDMap global. + // If team filtering is set by policy then pull down and retrieve team IDs as needed. This loads the TeamIDMap global. // Note that the call(s) to ScraperDirectoryAndConfigSanity() above will preload the team ID map from the persisted file // if it exists, so this will minimize the work that DownloadProjectTeamFiles() has to do, unless explorer mode (fExplorer) is true. if (REQUIRE_TEAM_WHITELIST_MEMBERSHIP || fExplorer) DownloadProjectTeamFiles(projectWhitelist); @@ -1442,7 +1442,7 @@ bool ScraperDirectoryAndConfigSanity() } // If network policy is set to filter on whitelisted teams, then load team ID map from file. This will prevent the heavyweight - // team file downloads for projects whose team ID's have already been found and stored, unless explorer mode (fExplorer) is true. + // team file downloads for projects whose team IDs have already been found and stored, unless explorer mode (fExplorer) is true. if (REQUIRE_TEAM_WHITELIST_MEMBERSHIP) { LOCK(cs_TeamIDMap); diff --git a/src/qt/consolidateunspentwizardselectinputspage.cpp b/src/qt/consolidateunspentwizardselectinputspage.cpp index b2e1a050fd..f525b5780d 100644 --- a/src/qt/consolidateunspentwizardselectinputspage.cpp +++ b/src/qt/consolidateunspentwizardselectinputspage.cpp @@ -504,7 +504,7 @@ void ConsolidateUnspentWizardSelectInputsPage::updateLabels() emit setDefaultAddressSignal(QString()); } - // This provids the trigger to update the fields from the labels, since they are QLabels and don't have appropriate + // This provides the trigger to update the fields from the labels, since they are QLabels and don't have appropriate // internal signals. emit updateFieldsSignal(); diff --git a/src/qt/decoration.h b/src/qt/decoration.h index 380e9b12c4..defab1de23 100644 --- a/src/qt/decoration.h +++ b/src/qt/decoration.h @@ -19,7 +19,7 @@ namespace GRC { //! //! \brief Set a widget's scaled font size in integer points. //! -//! This accomodates macOS which scales a point unit with a different base DPI +//! This accommodates macOS which scales a point unit with a different base DPI //! than other operating systems. //! void ScaleFontPointSize(QWidget* widget, int point_size); @@ -27,7 +27,7 @@ void ScaleFontPointSize(QWidget* widget, int point_size); //! //! \brief Set a widget's scaled font size in floating-point points. //! -//! This accomodates macOS which scales a point unit with a different base DPI +//! This accommodates macOS which scales a point unit with a different base DPI //! than other operating systems. //! void ScaleFontPointSizeF(QWidget* widget, double point_size); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index aeddd2ad25..2371db8df8 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -400,7 +400,7 @@ UniValue sendalert(const UniValue& params, bool fHelp) " -----> is the maximum applicable internal client version\n" " ---> is integer priority number\n" " ---------> is the alert id\n" - "[cancelupto] -> cancels all alert id's up to this number\n" + "[cancelupto] -> cancels all alert ids up to this number\n" "\n" "Returns true or false\n"); From 770ca1f58f70c96c97947304a325ec8a94efe788 Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 26 May 2021 19:24:19 -0400 Subject: [PATCH 130/280] Grammar and Typos Linux is always capitalized lets -> let's users -> user's --- CHANGELOG.md | 2 +- depends/packages.md | 2 +- src/gridcoin/scraper/http.cpp | 2 +- src/gridcoin/scraper/scraper.cpp | 2 +- src/gridcoin/upgrade.cpp | 2 +- src/leveldb/port/atomic_pointer.h | 2 +- src/qt/macnotificationhandler.mm | 4 ++-- src/qt/notificator.cpp | 2 +- src/rpc/rawtransaction.cpp | 2 +- src/rpc/server.cpp | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3233e7960d..8acb7d959e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -697,7 +697,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - getsupervotes - /var/lib/boinc/ as a valid boinc path on Linux (@rsparlin) - Stress testing script (@Foggyx420) - - refhash command also on linux (@jamescowens) + - refhash command also on Linux (@jamescowens) - Documentation for out of source build (@thecharlatan) ### Changed diff --git a/depends/packages.md b/depends/packages.md index 7c80362509..b60665c4f1 100644 --- a/depends/packages.md +++ b/depends/packages.md @@ -59,7 +59,7 @@ the modifications specific to that case. For example: Universal: $(package)_cc=gcc Linux only: $(package)_linux_cc=gcc x86_64 only: $(package)_x86_64_cc = gcc - x86_64 linux only: $(package)_x86_64_linux_cc = gcc + x86_64 Linux only: $(package)_x86_64_linux_cc = gcc These variables may be set to override or append their default values. diff --git a/src/gridcoin/scraper/http.cpp b/src/gridcoin/scraper/http.cpp index 791b224640..a187203560 100644 --- a/src/gridcoin/scraper/http.cpp +++ b/src/gridcoin/scraper/http.cpp @@ -14,7 +14,7 @@ #if LIBCURL_VERSION_NUM >= 0x073d00 /* In libcurl 7.61.0, support was added for extracting the time in plain - microseconds. Lets make this so we can allow a user using own depends + microseconds. Let's make this so we can allow a user using own depends for compile. */ #define timetype curl_off_t #define timeopt CURLINFO_TOTAL_TIME_T diff --git a/src/gridcoin/scraper/scraper.cpp b/src/gridcoin/scraper/scraper.cpp index bf24ff7f17..f6a82670ea 100755 --- a/src/gridcoin/scraper/scraper.cpp +++ b/src/gridcoin/scraper/scraper.cpp @@ -2308,7 +2308,7 @@ bool ProcessProjectRacFileByCPID(const std::string& project, const fs::path& fil bOnTeamWhitelist = true; } - //If not continue the while loop and do not put the users stats for that project in the outputstatistics file. + //If not continue the while loop and do not put the user's stats for that project in the outputstatistics file. if (!bOnTeamWhitelist) continue; } diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index b2299f1ecc..6d90e1ee18 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -458,7 +458,7 @@ bool Upgrade::ExtractSnapshot() entries = zip_get_num_entries(ZipArchive, 0); - // Lets scan for total size uncompressed so we can do a detailed progress for the watching user + // Let's scan for total size uncompressed so we can do a detailed progress for the watching user for (j = 0; j < (uint64_t)entries; j++) { if (zip_stat_index(ZipArchive, j, 0, &ZipStat) == 0) diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h index d79a02230d..c3c36f8a14 100644 --- a/src/leveldb/port/atomic_pointer.h +++ b/src/leveldb/port/atomic_pointer.h @@ -8,7 +8,7 @@ // - If is present (on newer versions of gcc, it is), we use // a -based AtomicPointer. However we prefer the memory // barrier based version, because at least on a gcc 4.4 32-bit build -// on linux, we have encountered a buggy implementation. +// on Linux, we have encountered a buggy implementation. // Also, some implementations are much slower than a memory-barrier // based implementation (~16ns for based acquire-load vs. ~1ns for // a barrier based acquire-load). diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 8bb9b887a1..42111932a6 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -5,7 +5,7 @@ void MacNotificationHandler::showNotification(const QString &title, const QString &text) { - // check if users OS has support for NSUserNotification + // check if user's OS has support for NSUserNotification if(this->hasUserNotificationCenterSupport()) { // okay, seems like 10.8+ QByteArray utf8 = title.toUtf8(); @@ -48,7 +48,7 @@ { Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); - // check if users OS has support for NSUserNotification + // check if user's OS has support for NSUserNotification if(possibleClass!=nil) { return true; } diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index dbd82f909e..9e030f87b3 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -55,7 +55,7 @@ trayIcon(_trayIcon) } #endif #ifdef Q_OS_MAC - // check if users OS has support for NSUserNotification + // check if user's OS has support for NSUserNotification if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { mode = UserNotificationCenter; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 0d727c7023..fbfb060e0b 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -981,7 +981,7 @@ UniValue consolidatemsunspent(const UniValue& params, bool fHelp) // Add to our input list umultimapInputs.insert(std::make_pair(txout.nValue, std::make_pair(tx.GetHash(), j))); - // shouldn't ever surpass this but lets just be safe! + // shouldn't ever surpass this but let's just be safe! if (umultimapInputs.size() >= (unsigned int) nMaxInputs) fComplete = true; } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 1a11797313..e479896629 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -877,7 +877,7 @@ UniValue CRPCTable::execute(const std::string& strMethod, const UniValue& params if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); - // Lets add a optional display if BCLog::LogFlags::RPC is set to show how long it takes + // Let's add a optional display if BCLog::LogFlags::RPC is set to show how long it takes // the rpc commands to be performed in milliseconds. We will do this only on successful // calls not exceptions. try From 36835f2932675e6f4d4b0c33a8d3bb1682f5897d Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 26 May 2021 19:39:03 -0400 Subject: [PATCH 131/280] Qt: Add antialiasing to traffic graph widget refer to https://github.com/bitcoin/bitcoin/pull/16153 --- src/qt/trafficgraphwidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 8089abacf6..d63be2b7e3 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -113,6 +113,7 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *) } } + painter.setRenderHint(QPainter::Antialiasing); if(!vSamplesIn.empty()) { QPainterPath p; paintPath(p, vSamplesIn); From e36beced0951f1c61ca8cc3e437d06300043a8de Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Thu, 27 May 2021 18:13:05 +0000 Subject: [PATCH 132/280] Correct spelling --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d6e3719a9f..d2e5a7b14b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -5,7 +5,7 @@ title: '[bug] ' labels: bug --- - + **Steps to reproduce:** - + **Screenshots** @@ -39,4 +39,4 @@ If you are not on the latest Leisure release please try updating to that and see - Disk Type (HD/SDD): **Extra information** - + From 1c683ec5f63bbb016943228075347176916be7fc Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Thu, 27 May 2021 18:13:56 +0000 Subject: [PATCH 133/280] Correct spelling --- .github/ISSUE_TEMPLATE/feature_request.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 3fed9cc59a..c889da8e56 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,11 +1,11 @@ --- name: 🚀 Feature Request about: Suggest an idea for this project -title: '[feat] ' +title: '[feature] ' labels: enhancement --- - + **Describe Alternatives** - + **Related Code** From 80db6a8c93ad337af9878a6e6eb72f4d59b0c82a Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 28 May 2021 06:04:14 +0000 Subject: [PATCH 134/280] Add config.yml with contact information --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..3e1c85435a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Gridcoin Discord + url: https://discord.gg/jf9XX4a + about: Please go here if you have any general issues, that aren't bug reports. We can assist you much faster there. + - name: Gridcoin Subreddit + url: https://old.reddit.com/r/gridcoin + about: Alternative platform. From ae6581c864f680f4ea1a56fc8751b8989c06974b Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Wed, 2 Jun 2021 05:57:40 +0000 Subject: [PATCH 135/280] remove colon on heading, remove title --- .github/ISSUE_TEMPLATE/bug_report.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d2e5a7b14b..f3993a9de1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,6 @@ --- name: 🐜 Bug Report about: Create a report to help us improve -title: '[bug] ' labels: bug --- @@ -15,7 +14,7 @@ labels: bug # Bug Report -**Current behavior:** +**Current behavior** **Expected behavior** From 3e2931c3edf34ca67510031aeb075cf665869d54 Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Wed, 2 Jun 2021 05:58:19 +0000 Subject: [PATCH 136/280] change to old.reddit to reddit --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3e1c85435a..f6110705de 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,5 +4,5 @@ contact_links: url: https://discord.gg/jf9XX4a about: Please go here if you have any general issues, that aren't bug reports. We can assist you much faster there. - name: Gridcoin Subreddit - url: https://old.reddit.com/r/gridcoin + url: https://reddit.com/r/gridcoin about: Alternative platform. From b179cac9ed0a0d3049c6f978e75d814853954e3d Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Wed, 2 Jun 2021 05:59:59 +0000 Subject: [PATCH 137/280] apply suggestions --- .github/ISSUE_TEMPLATE/feature_request.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index c889da8e56..4af4600b16 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,18 +1,15 @@ --- name: 🚀 Feature Request about: Suggest an idea for this project -title: '[feature] ' labels: enhancement --- - + - - # Feature Request **Describe the Feature Request** From e934d05324514554fd594136ebd255854e186007 Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 12:22:27 -0400 Subject: [PATCH 138/280] Update curl version 7.74.0 to 7.77.0 --- depends/packages/curl.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/packages/curl.mk b/depends/packages/curl.mk index 7ebc5c5e02..283c3da51d 100644 --- a/depends/packages/curl.mk +++ b/depends/packages/curl.mk @@ -1,9 +1,9 @@ package=curl GCCFLAGS?= -$(package)_version=7.74.0 +$(package)_version=7.77.0 $(package)_download_path=https://curl.haxx.se/download/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=e56b3921eeb7a2951959c02db0912b5fcd5fdba5aca071da819e1accf338bbd7 +$(package)_sha256_hash=b0a3428acb60fa59044c4d0baae4e4fc09ae9af1d8a3aa84b2e3fbcd99841f77 $(package)_dependencies=openssl define $(package)_set_vars From 20c5031e843c3be3b17e486fd0a2d6ebf41e639f Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 12:41:51 -0400 Subject: [PATCH 139/280] Update QT to 5.12.11 Fixes a lot of bugs, including ones affecting upstream Bitcoin Bigsur drawing patch no longer needed Ref: https://github.com/bitcoin/bitcoin/pull/22054 --- depends/packages/qt.mk | 13 ++++----- depends/patches/qt/fix_bigsur_drawing.patch | 31 --------------------- 2 files changed, 6 insertions(+), 38 deletions(-) delete mode 100644 depends/patches/qt/fix_bigsur_drawing.patch diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 80faa16abf..df8075d0e4 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,9 +1,9 @@ PACKAGE=qt -$(package)_version=5.12.10 +$(package)_version=5.12.11 $(package)_download_path=https://download.qt.io/official_releases/qt/5.12/$($(package)_version)/submodules $(package)_suffix=everywhere-src-$($(package)_version).tar.xz $(package)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=8088f174e6d28e779516c083b6087b6a9e3c8322b4bc161fd1b54195e3c86940 +$(package)_sha256_hash=1c1b4e33137ca77881074c140d54c3c9747e845a31338cfe8680f171f0bc3a39 $(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon $(package)_qt_libs=corelib network widgets gui plugins testlib concurrent $(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no-xlib.patch @@ -13,18 +13,18 @@ $(package)_patches+= fix_lib_paths.patch fix_android_pch.patch $(package)_patches+= fix_bigsur_drawing.patch qtbase-moc-ignore-gcc-macro.patch subdirs.pro $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=e1de58ed108b7e0a138815ea60fd46a2c4e1fc31396a707e5630e92de79c53de +$(package)_qttranslations_sha256_hash=577b0668a777eb2b451c61e8d026d79285371597ce9df06b6dee6c814164b7c3 $(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=b0cfa6e7aac41b7c61fc59acc04843d7a98f9e1840370611751bcfc1834a636c +$(package)_qttools_sha256_hash=98b2aaca230458f65996f3534fd471d2ffd038dd58ac997c0589c06dc2385b4f # Gridcoin uses charts for the voting features: $(package)_qtcharts_file_name=qtcharts-$($(package)_suffix) -$(package)_qtcharts_sha256_hash=ac297e27844d3193205db02a22d68a9af7d79f416891617f9389ba4d29aacd32 +$(package)_qtcharts_sha256_hash=8567502335913a45dbe47c5b493974b48c2049dc07ab5a2a273ddfdcf43c002c # Gridcoin displays SVG images in the GUI: $(package)_qtsvg_file_name=qtsvg-$($(package)_suffix) -$(package)_qtsvg_sha256_hash=b5fff4cfe158e17cb3f182c521aa6385f56e158b816d6dff1de915213cfa334b +$(package)_qtsvg_sha256_hash=7a6857a2f68cfbebb9f791396b401a98e951c9bff9bfeb1b5b01914c3ea1a0ed $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) @@ -258,7 +258,6 @@ define $(package)_preprocess_cmds patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \ patch -p1 -i $($(package)_patch_dir)/no_sdk_version_check.patch && \ patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \ - patch -p1 -i $($(package)_patch_dir)/fix_bigsur_drawing.patch && \ patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.patch && \ cp $($(package)_patch_dir)/subdirs.pro subdirs.pro && \ sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ diff --git a/depends/patches/qt/fix_bigsur_drawing.patch b/depends/patches/qt/fix_bigsur_drawing.patch deleted file mode 100644 index 98c0c6be30..0000000000 --- a/depends/patches/qt/fix_bigsur_drawing.patch +++ /dev/null @@ -1,31 +0,0 @@ -Fix GUI stuck on Big Sur - -See: - - https://github.com/bitcoin-core/gui/issues/249 - - https://github.com/bitcoin/bitcoin/pull/21495 - - https://bugreports.qt.io/browse/QTBUG-87014 - -We should be able to drop this once we are using one of the following versions: - - Qt 5.12.11 or later, see upstream commit: c5d904639dbd690a36306e2b455610029704d821 - - Qt 5.15.3 or later, see upstream commit: 2cae34354bd41ae286258c7a6b3653b746e786ae - ---- a/qtbase/src/plugins/platforms/cocoa/qnsview_drawing.mm -+++ b/qtbase/src/plugins/platforms/cocoa/qnsview_drawing.mm -@@ -95,8 +95,15 @@ - // by AppKit at a point where we've already set up other parts of the platform plugin - // based on the presence of layers or not. Once we've rewritten these parts to support - // dynamically picking up layer enablement we can let AppKit do its thing. -- return QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave -- && QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave; -+ -+ if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSBigSur) -+ return true; // Big Sur always enables layer-backing, regardless of SDK -+ -+ if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave -+ && QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave) -+ return true; // Mojave and Catalina enable layers based on the app's SDK -+ -+ return false; // Prior versions needed explicitly enabled layer backing - } - - - (BOOL)layerExplicitlyRequested From 5402d6b1681bb26ea029ae860dde861d682114e2 Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 14:02:09 -0400 Subject: [PATCH 140/280] Remove more references to Travis before_install.sh might be able to be removed entirely, see (https://github.com/bitcoin/bitcoin/pull/20691/commits/09d105ef0f8b4b06bf248721a1209c9e16e9db75). --- ci/lint/06_script.sh | 8 ++++---- ci/test/00_setup_env_arm.sh | 2 +- ci/test/03_before_install.sh | 10 +--------- test/lint/lint-shell.sh | 6 +++--- test/lint/lint-whitespace.sh | 14 +++++++------- 5 files changed, 16 insertions(+), 24 deletions(-) diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh index a9662d286c..7042896fc2 100755 --- a/ci/lint/06_script.sh +++ b/ci/lint/06_script.sh @@ -6,8 +6,8 @@ export LC_ALL=C -if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then - test/lint/commit-script-check.sh $TRAVIS_COMMIT_RANGE +if [ "$CI_EVENT_TYPE" = "pull_request" ]; then + test/lint/commit-script-check.sh $CI_COMMIT_RANGE fi if [ "$EVENT_TYPE" = "pull_request" ]; then @@ -22,8 +22,8 @@ fi #test/lint/check-rpc-mappings.py . test/lint/lint-all.sh -#if [ "$TRAVIS_REPO_SLUG" = "gridcoin-community/Gridcoin-Research" ] && [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then +#if [ "$CI_REPO_SLUG" = "gridcoin-community/Gridcoin-Research" ] && [ "$CI_EVENT_TYPE" = "cron" ]; then # git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit -# travis_retry gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $( Date: Wed, 2 Jun 2021 15:16:25 -0400 Subject: [PATCH 141/280] Fix curl ssl options --- depends/packages/curl.mk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/depends/packages/curl.mk b/depends/packages/curl.mk index 283c3da51d..f842d67919 100644 --- a/depends/packages/curl.mk +++ b/depends/packages/curl.mk @@ -12,9 +12,10 @@ define $(package)_set_vars $(package)_config_opts+= --without-brotli $(package)_config_opts+= --libdir=$($($(package)_type)_prefix)/lib $(package)_config_opts_release+=--disable-debug-mode - $(package)_config_opts_linux+=--with-pic + $(package)_config_opts_linux+=--with-pic -with-openssl # Disable OpenSSL for Windows and use native SSL stack (SSPI/Schannel): - $(package)_config_opts_mingw32+= --with-winssl --without-ssl + $(package)_config_opts_mingw32+= --with-schannel + $(package)_config_opts_darwin+= --with-secure-transport # This extra flag for macOS is necessary as curl will append a -mmacosx-version-min=10.8 otherwise # which will cause the linker to fail as it cannot optimize away a __builtin_available(MacOS 10.11...) call # which requires a link to compiler runtime library. From 80085fd47dfc4f825cfcad00a9ad800f22e0ac2a Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 16:57:13 -0400 Subject: [PATCH 142/280] Update cppcheck linter to c++17 --- test/lint/extended-lint-cppcheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lint/extended-lint-cppcheck.sh b/test/lint/extended-lint-cppcheck.sh index 20021d8605..dc7e9ef650 100755 --- a/test/lint/extended-lint-cppcheck.sh +++ b/test/lint/extended-lint-cppcheck.sh @@ -66,7 +66,7 @@ function join_array { ENABLED_CHECKS_REGEXP=$(join_array "|" "${ENABLED_CHECKS[@]}") IGNORED_WARNINGS_REGEXP=$(join_array "|" "${IGNORED_WARNINGS[@]}") WARNINGS=$(git ls-files -- "*.cpp" "*.h" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" | \ - xargs cppcheck --enable=all -j "$(getconf _NPROCESSORS_ONLN)" --language=c++ --std=c++11 --template=gcc -D__cplusplus -DCLIENT_VERSION_BUILD -DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR -DDEBUG -I src/ -q 2>&1 | sort -u | \ + xargs cppcheck --enable=all -j "$(getconf _NPROCESSORS_ONLN)" --language=c++ --std=c++17 --template=gcc -D__cplusplus -DCLIENT_VERSION_BUILD -DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR -DDEBUG -I src/ -q 2>&1 | sort -u | \ grep -E "${ENABLED_CHECKS_REGEXP}" | \ grep -vE "${IGNORED_WARNINGS_REGEXP}") if [[ ${WARNINGS} != "" ]]; then From 5d95bd91bd45f0ccd8bee9218656e3defd0d3b9e Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 17:05:02 -0400 Subject: [PATCH 143/280] test: Mention commit id in scripted diff error ref https://github.com/bitcoin/bitcoin/pull/20069 --- test/lint/commit-script-check.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh index ff3f784437..827c978bed 100755 --- a/test/lint/commit-script-check.sh +++ b/test/lint/commit-script-check.sh @@ -37,7 +37,7 @@ for commit in $(git rev-list --reverse $1); do git reset --quiet --hard HEAD else if git rev-list "--format=%b" -n1 $commit | grep -q '^-\(BEGIN\|END\)[ a-zA-Z]*-$'; then - echo "Error: script block marker but no scripted-diff in title" + echo "Error: script block marker but no scripted-diff in title of commit $commit" echo "Failed" RET=1 fi From f9ee87f940742f3c73af32312587a96a9c8654cb Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 17:08:18 -0400 Subject: [PATCH 144/280] typos reenable -> re-enable imbedded -> embedded --- CHANGELOG.md | 2 +- src/gridcoin/backup.cpp | 10 +++++----- src/qt/coincontroldialog.cpp | 2 +- src/qt/consolidateunspentwizardselectinputspage.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a304dd79..ffdd04b81b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -392,7 +392,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - Fix lingering peers.dat temp files and clean up remaining paths #1582 (@cyrossignol) - Fix incorrect beacon length warning in GUI transaction list #1585 (@cyrossignol) - Fix default config file line endings on Windows #1587 (@cyrossignol) - - Reenable Travis builds for MacOS #1591 (@jamescowens) + - Re-enable Travis builds for MacOS #1591 (@jamescowens) - Correct peer detail info background color #1593 (@jamescowens) - Fix exception in debug3 mode #1598 (@cyrossignol) - Fix deadlock in "getmininginfo" RPC function #1596 (@cyrossignol) diff --git a/src/gridcoin/backup.cpp b/src/gridcoin/backup.cpp index 260b95d224..e955217fa1 100644 --- a/src/gridcoin/backup.cpp +++ b/src/gridcoin/backup.cpp @@ -265,7 +265,7 @@ bool GRC::MaintainBackups(fs::path wallet_backup_path, std::vector { std::string filename = iter.path().filename().string(); - int64_t imbedded_file_time = 0; + int64_t embedded_file_time = 0; size_t found_pos = filename.find("-"); @@ -276,10 +276,10 @@ bool GRC::MaintainBackups(fs::path wallet_backup_path, std::vector { std::string datetime = filename.substr(found_pos + 1, filename.size() - (found_pos + 1)); - imbedded_file_time = ParseISO8601DateTime(datetime); + embedded_file_time = ParseISO8601DateTime(datetime); - // If ParseISO8601DateTime can't parse the imbedded datetime string, it will return 0. - if (!imbedded_file_time) + // If ParseISO8601DateTime can't parse the embedded datetime string, it will return 0. + if (!embedded_file_time) { LogPrintf("WARN: MaintainBackups: Unable to parse date-time in backup filename." "Ignoring time retention for this file."); @@ -294,7 +294,7 @@ bool GRC::MaintainBackups(fs::path wallet_backup_path, std::vector if (retention_by_days > 0) { - if (i >= retention_by_num && (!imbedded_file_time || (imbedded_file_time < retention_cutoff_time))) + if (i >= retention_by_num && (!embedded_file_time || (embedded_file_time < retention_cutoff_time))) { remove_file = true; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 6c97b5e5e9..48d0081e00 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -352,7 +352,7 @@ bool CoinControlDialog::filterInputsByValue(const bool& less, const CAmount& inp ++input_count; } - // Reenable update signals. + // Re-enable update signals. ui->treeWidget->setEnabled(true); CoinControlDialog::updateLabels(model, coinControl, payAmounts, this); diff --git a/src/qt/consolidateunspentwizardselectinputspage.cpp b/src/qt/consolidateunspentwizardselectinputspage.cpp index f525b5780d..3f14be1bad 100644 --- a/src/qt/consolidateunspentwizardselectinputspage.cpp +++ b/src/qt/consolidateunspentwizardselectinputspage.cpp @@ -254,7 +254,7 @@ bool ConsolidateUnspentWizardSelectInputsPage::filterInputsByValue(const bool& l ++input_count; } - // Reenable update signals. + // Re-enable update signals. ui->treeWidget->setEnabled(true); // If the number of inputs selected was limited, then true is returned. From d93009f965a16e54257d7a9381f2e1a10eedbb5e Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 17:18:43 -0400 Subject: [PATCH 145/280] ci: Drop Travis-specific workaround for shellcheck Ref https://github.com/bitcoin/bitcoin/commit/c123892c2e47e3706f06820aba2454d494a39564 --- test/lint/lint-shell.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh index 9a26cd9c02..57cb7be583 100755 --- a/test/lint/lint-shell.sh +++ b/test/lint/lint-shell.sh @@ -8,14 +8,6 @@ export LC_ALL=C -# The shellcheck binary segfault/coredumps in Travis with LC_ALL=C -# It does not do so in Ubuntu 14.04, 16.04, 18.04 in versions 0.3.3, 0.3.7, 0.4.6 -# respectively. So export LC_ALL=C is set as required by lint-shell-locale.sh -# but unset here in case of running in Travis. -if [ "$TRAVIS" = "true" ]; then - unset LC_ALL -fi - # Disabled warnings: disabled=( SC2046 # Quote this to prevent word splitting. From 5030bcc2cf583b08f5011f6761f1d5fa407ea4ba Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 17:20:33 -0400 Subject: [PATCH 146/280] ci: Drop Travis-specific way to set COMMIT_RANGE variable Ref: https://github.com/bitcoin/bitcoin/commit/10af252d97532843b26505d215f6e975f4b21672 --- test/lint/lint-whitespace.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh index d8bdb0a8d7..588a3f86e8 100755 --- a/test/lint/lint-whitespace.sh +++ b/test/lint/lint-whitespace.sh @@ -22,14 +22,6 @@ while getopts "?" opt; do esac done -if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then - if [ -n "$1" ]; then - TRAVIS_COMMIT_RANGE="HEAD~$1...HEAD" - else - TRAVIS_COMMIT_RANGE="HEAD" - fi -fi - showdiff() { if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then echo "Failed to get a diff" From 3211cc331b50fed1f42015c5278199b8ece4293f Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 17:22:29 -0400 Subject: [PATCH 147/280] Update spellcheck ignore words --- test/lint/lint-spelling.ignore-words.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt index 8a5bee56b0..3577220a37 100644 --- a/test/lint/lint-spelling.ignore-words.txt +++ b/test/lint/lint-spelling.ignore-words.txt @@ -9,6 +9,7 @@ copyable cachable errorstring keyserver +keypair homogenous setban hist @@ -19,3 +20,5 @@ unser nnumber fo dout +nin +inout From d1e1e9fa7bcf5963019e64429d4ea16656edc949 Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 18:18:23 -0400 Subject: [PATCH 148/280] Bump python to 3.6 Python 3.5 went EOL in September 2020 --- .github/workflows/ci.yml | 2 +- configure.ac | 2 +- doc/build-openbsd.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1672ad999f..2007957431 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: - name: setup-python uses: actions/setup-python@v2 with: - python-version: 3.5 + python-version: 3.6 - name: lint continue-on-error: true run: | diff --git a/configure.ac b/configure.ac index 8b583a41ff..dbdbff1c2b 100755 --- a/configure.ac +++ b/configure.ac @@ -84,7 +84,7 @@ AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893) -AC_PATH_PROGS([PYTHON], [python3.6 python3.5 python3.4 python3 python2.7 python2 python]) +AC_PATH_PROGS([PYTHON], [python3.6 python3.7 python3.8 python3 python2.7 python2 python]) AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG([GIT], [git]) AC_PATH_PROG(CCACHE,ccache) diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index 93bfb26b5d..fe8a4d4eda 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -15,7 +15,7 @@ Run the following as root to install the base dependencies for building: pkg_add gmake libtool libevent pkg_add autoconf # (select highest version, e.g. 2.69) pkg_add automake # (select highest version, e.g. 1.15) -pkg_add python # (select highest version, e.g. 3.5) +pkg_add python # (select highest version, e.g. 3.6) ``` The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler. From 5b2149cb76fa44cf435a54f530cfd75bcb118671 Mon Sep 17 00:00:00 2001 From: barton26 Date: Wed, 2 Jun 2021 18:35:50 -0400 Subject: [PATCH 149/280] Fix badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7edf219e1e..bd64e58009 100644 --- a/README.md +++ b/README.md @@ -85,4 +85,4 @@ Build Status | Development | Staging | Master | |----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| -| [![Build Status](https://travis-ci.org/gridcoin-community/Gridcoin-Research.svg?branch=development)](https://travis-ci.org/gridcoin-community/Gridcoin-Research) | [![Build Status](https://travis-ci.org/gridcoin-community/Gridcoin-Research.svg?branch=staging)](https://travis-ci.org/gridcoin-community/Gridcoin-Research) | [![Build Status](https://travis-ci.org/gridcoin-community/Gridcoin-Research.svg?branch=master)](https://travis-ci.org/gridcoin-community/Gridcoin-Research) | +| ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=development) | ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=staging) | ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=staging) | From 7446c6d854d716a231c1aa88318d666dc8534ede Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 3 Jun 2021 14:38:43 -0400 Subject: [PATCH 150/280] Update ci/lint/06_script.sh Co-authored-by: div72 <60045611+div72@users.noreply.github.com> --- ci/lint/06_script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh index 7042896fc2..f5e34afd0d 100755 --- a/ci/lint/06_script.sh +++ b/ci/lint/06_script.sh @@ -24,6 +24,6 @@ test/lint/lint-all.sh #if [ "$CI_REPO_SLUG" = "gridcoin-community/Gridcoin-Research" ] && [ "$CI_EVENT_TYPE" = "cron" ]; then # git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit -# CI_retry gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $( Date: Thu, 3 Jun 2021 14:39:20 -0400 Subject: [PATCH 151/280] Update ci/lint/06_script.sh Co-authored-by: div72 <60045611+div72@users.noreply.github.com> --- ci/lint/06_script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh index f5e34afd0d..70ddab4704 100755 --- a/ci/lint/06_script.sh +++ b/ci/lint/06_script.sh @@ -6,7 +6,7 @@ export LC_ALL=C -if [ "$CI_EVENT_TYPE" = "pull_request" ]; then +if [ "$GITHUB_EVENT_TYPE" = "pull_request" ]; then test/lint/commit-script-check.sh $CI_COMMIT_RANGE fi From 987afac36df0e6f949eb005d52265df3d419b290 Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 3 Jun 2021 15:11:30 -0400 Subject: [PATCH 152/280] Revert "ci: Drop Travis-specific way to set COMMIT_RANGE variable" This reverts commit 5030bcc2cf583b08f5011f6761f1d5fa407ea4ba. --- test/lint/lint-whitespace.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh index 588a3f86e8..d8bdb0a8d7 100755 --- a/test/lint/lint-whitespace.sh +++ b/test/lint/lint-whitespace.sh @@ -22,6 +22,14 @@ while getopts "?" opt; do esac done +if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then + if [ -n "$1" ]; then + TRAVIS_COMMIT_RANGE="HEAD~$1...HEAD" + else + TRAVIS_COMMIT_RANGE="HEAD" + fi +fi + showdiff() { if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then echo "Failed to get a diff" From 603b67935103f6ce7218b97cc9b6280dc5a03d78 Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 3 Jun 2021 15:14:16 -0400 Subject: [PATCH 153/280] Suggestion --- test/lint/lint-whitespace.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh index d8bdb0a8d7..0fa4e2c6f6 100755 --- a/test/lint/lint-whitespace.sh +++ b/test/lint/lint-whitespace.sh @@ -13,32 +13,32 @@ while getopts "?" opt; do case $opt in ?) echo "Usage: $0 [N]" - echo " TRAVIS_COMMIT_RANGE='' $0" + echo " COMMIT_RANGE='' $0" echo " $0 -?" echo "Checks unstaged changes, the previous N commits, or a commit range." - echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' $0" + echo "COMMIT_RANGE='47ba2c3...ee50c9e' $0" exit 0 ;; esac done -if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then +if [ -z "${COMMIT_RANGE}" ]; then if [ -n "$1" ]; then - TRAVIS_COMMIT_RANGE="HEAD~$1...HEAD" + COMMIT_RANGE="HEAD~$1...HEAD" else - TRAVIS_COMMIT_RANGE="HEAD" + COMMIT_RANGE="HEAD" fi fi showdiff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then + if ! git diff -U0 "${COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then echo "Failed to get a diff" exit 1 fi } showcodediff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then + if ! git diff -U0 "${COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/" ":(exclude)src/qt/locale/"; then echo "Failed to get a diff" exit 1 fi From e1542b74362c976cc81ceaf3b168677512c1bd03 Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 3 Jun 2021 15:21:49 -0400 Subject: [PATCH 154/280] Add suggestion --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1672ad999f..71b5257f4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ on: [push, pull_request] env: EVENT_TYPE: ${{ github.event_name }} COMMIT_COUNT: ${{ github.event.commits.length }} + CI_REPO_SLUG: ${{ github.repository }} jobs: test-linux: name: ${{ matrix.name }} From 4ec53993524f07556f01c9052f55abeaca60cac5 Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 3 Jun 2021 17:39:24 -0400 Subject: [PATCH 155/280] Update ci/lint/06_script.sh Co-authored-by: div72 <60045611+div72@users.noreply.github.com> --- ci/lint/06_script.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh index 70ddab4704..0a0687da80 100755 --- a/ci/lint/06_script.sh +++ b/ci/lint/06_script.sh @@ -6,9 +6,6 @@ export LC_ALL=C -if [ "$GITHUB_EVENT_TYPE" = "pull_request" ]; then - test/lint/commit-script-check.sh $CI_COMMIT_RANGE -fi if [ "$EVENT_TYPE" = "pull_request" ]; then test/lint/commit-script-check.sh $(git rev-parse HEAD~$COMMIT_COUNT)..$GITHUB_SHA From 5febafc2e1f5547ca5f434bd5907a8e538e2a22a Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 4 Jun 2021 06:29:39 +0000 Subject: [PATCH 156/280] remove markdown link in comment --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f3993a9de1..959380e742 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,8 +7,8 @@ labels: bug +* Gridcoin on reddit https://www.reddit.com/r/gridcoin/ +* Discord https://discord.gg/jf9XX4a --> From 0d7c334b08483d7ade14fbdeb94b43f380b767fe Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 4 Jun 2021 06:30:16 +0000 Subject: [PATCH 157/280] remove markdown link in comment --- .github/ISSUE_TEMPLATE/feature_request.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 4af4600b16..568e0c196e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,8 +7,8 @@ labels: enhancement +* Gridcoin on reddit https://www.reddit.com/r/gridcoin/ +* Discord https://discord.gg/jf9XX4a --> # Feature Request From 0e81b34d90740b222e5f3dfe474483737e1ab71b Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 29 May 2021 18:32:56 -0400 Subject: [PATCH 158/280] Baseline ArgsManager port --- src/Makefile.am | 7 + src/alert.cpp | 2 +- src/chainparamsbase.cpp | 10 +- src/chainparamsbase.h | 5 + src/fs.cpp | 6 + src/fs.h | 11 + src/gridcoin/backup.cpp | 24 +- src/gridcoin/boinc.cpp | 2 +- src/gridcoin/gridcoin.cpp | 16 +- src/gridcoin/researcher.cpp | 23 +- src/gridcoin/scraper/http.cpp | 6 +- src/gridcoin/scraper/scraper.cpp | 8 +- src/gridcoin/scraper/scraper_net.cpp | 4 +- src/gridcoin/staking/kernel.cpp | 6 +- src/gridcoin/upgrade.cpp | 2 +- src/gridcoinresearchd.cpp | 71 +- src/init.cpp | 655 +++++++++----- src/init.h | 8 +- src/logging.cpp | 12 +- src/main.cpp | 10 +- src/main.h | 2 +- src/miner.cpp | 28 +- src/net.cpp | 32 +- src/net.h | 4 +- src/qt/bitcoin.cpp | 81 +- src/qt/bitcoingui.cpp | 6 +- src/qt/clientmodel.cpp | 2 +- src/qt/diagnosticsdialog.cpp | 2 +- src/qt/guiutil.cpp | 17 +- src/qt/guiutil.h | 3 +- src/qt/intro.cpp | 10 +- src/qt/optionsmodel.cpp | 20 +- src/qt/transactionfilterproxy.cpp | 2 +- src/qt/transactionrecord.cpp | 2 +- src/qt/upgradeqt.cpp | 2 +- src/rpc/blockchain.cpp | 15 +- src/rpc/client.cpp | 8 +- src/rpc/rawtransaction.cpp | 4 +- src/rpc/server.cpp | 33 +- src/rpc/server.h | 2 +- src/script.cpp | 2 +- src/sync.h | 203 ++++- src/test/dos_tests.cpp | 10 +- src/test/getarg_tests.cpp | 245 ++--- src/test/gridcoin/researcher_tests.cpp | 77 +- src/test/gridcoin_tests.cpp | 3 +- src/test/test_gridcoin.cpp | 6 +- src/test/util_tests.cpp | 79 +- src/threadsafety.h | 69 +- src/txdb-leveldb.cpp | 6 +- src/util.cpp | 535 ----------- src/util.h | 170 +--- src/util/check.h | 75 ++ src/util/settings.cpp | 240 +++++ src/util/settings.h | 108 +++ src/util/string.cpp | 26 + src/util/string.h | 100 +++ src/util/system.cpp | 1141 ++++++++++++++++++++++++ src/util/system.h | 467 ++++++++++ src/validation.cpp | 4 +- src/wallet/db.cpp | 6 +- src/wallet/rpcwallet.cpp | 6 +- src/wallet/wallet.cpp | 10 +- src/wallet/walletdb.cpp | 4 +- 64 files changed, 3398 insertions(+), 1357 deletions(-) create mode 100644 src/util/check.h create mode 100644 src/util/settings.cpp create mode 100644 src/util/settings.h create mode 100644 src/util/string.cpp create mode 100644 src/util/string.h create mode 100644 src/util/system.cpp create mode 100644 src/util/system.h diff --git a/src/Makefile.am b/src/Makefile.am index 2edac1556e..1a2ce76acc 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -169,8 +169,12 @@ GRIDCOIN_CORE_H = \ txdb-leveldb.h \ ui_interface.h \ uint256.h \ + util/check.h \ util/reverse_iterator.h \ + util/settings.h \ util/strencodings.h \ + util/string.h \ + util/system.h \ util/threadnames.h \ util/time.h \ util.h \ @@ -259,7 +263,10 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \ sync.cpp \ txdb-leveldb.cpp \ uint256.cpp \ + util/settings.cpp \ util/strencodings.cpp \ + util/string.cpp \ + util/system.cpp \ util/threadnames.cpp \ util/time.cpp \ util.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp index 2b1194b06e..6af50048fb 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -259,7 +259,7 @@ bool CAlert::ProcessAlert(bool fThread) if(AppliesToMe()) { uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); - std::string strCmd = GetArg("-alertnotify", ""); + std::string strCmd = gArgs.GetArg("-alertnotify", ""); if (!strCmd.empty()) { // Alert text should be plain ascii coming from a trusted source, but to diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 962d50d4c7..7a48f8d095 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -6,11 +6,17 @@ #include "chainparamsbase.h" #include "tinyformat.h" +#include #include const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; +void SetupChainParamsBaseOptions(ArgsManager& argsman) +{ + argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: main, test", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); +} static std::unique_ptr globalChainBaseParams; @@ -23,9 +29,9 @@ const CBaseChainParams& BaseParams() std::unique_ptr CreateBaseChainParams(const std::string& chain) { if (chain == CBaseChainParams::MAIN) - return std::make_unique("", 32749); + return std::make_unique("", 15715); else if (chain == CBaseChainParams::TESTNET) - return std::make_unique("testnet", 32748); + return std::make_unique("testnet", 25715); else throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index d0cd6de30b..dec06fee71 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -41,6 +41,11 @@ class CBaseChainParams */ std::unique_ptr CreateBaseChainParams(const std::string& chain); +/** + *Set the arguments for chainparams + */ +void SetupChainParamsBaseOptions(ArgsManager& argsman); + /** * Return the currently selected parameters. This won't change after app * startup, except for unit tests. diff --git a/src/fs.cpp b/src/fs.cpp index 7b422b8d70..9faa826bbe 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -22,6 +22,12 @@ FILE *fopen(const fs::path& p, const char *mode) #endif } +fs::path AbsPathJoin(const fs::path& base, const fs::path& path) +{ + assert(base.is_absolute()); + return fs::absolute(path, base); +} + #ifndef WIN32 static std::string GetErrorReason() { diff --git a/src/fs.h b/src/fs.h index c713297d6e..581721128e 100644 --- a/src/fs.h +++ b/src/fs.h @@ -22,6 +22,17 @@ namespace fs = boost::filesystem; namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode); + /** + * Helper function for joining two paths + * + * @param[in] base Base path + * @param[in] path Path to combine with base + * @returns path unchanged if it is an absolute path, otherwise returns base joined with path. Returns base unchanged if path is empty. + * @pre Base path must be absolute + * @post Returned path will always be absolute + */ + fs::path AbsPathJoin(const fs::path& base, const fs::path& path); + class FileLock { public: diff --git a/src/gridcoin/backup.cpp b/src/gridcoin/backup.cpp index e955217fa1..b027b410fc 100644 --- a/src/gridcoin/backup.cpp +++ b/src/gridcoin/backup.cpp @@ -16,7 +16,7 @@ using namespace GRC; fs::path GRC::GetBackupPath() { fs::path defaultDir = GetDataDir() / "walletbackups"; - return GetArg("-backupdir", defaultDir.string()); + return gArgs.GetArg("-backupdir", defaultDir.string()); } std::string GRC::GetBackupFilename(const std::string& basename, const std::string& suffix) @@ -40,20 +40,20 @@ bool GRC::BackupsEnabled() { // If either of these configuration options is explicitly set to zero, // disable backups completely: - return GetArg("-walletbackupinterval", 1) > 0 - && GetArg("-walletbackupintervalsecs", 1) > 0; + return gArgs.GetArg("-walletbackupinterval", 1) > 0 + && gArgs.GetArg("-walletbackupintervalsecs", 1) > 0; } int64_t GRC::GetBackupInterval() { - int64_t backup_interval_secs = GetArg("-walletbackupintervalsecs", 86400); + int64_t backup_interval_secs = gArgs.GetArg("-walletbackupintervalsecs", 86400); // The deprecated -walletbackupinterval option specifies the backup interval // as the number of blocks that pass. If someone still uses this in a config // file, we'll honor it for now: // - if (mapArgs.count("-walletbackupinterval")) { - backup_interval_secs = GetArg("-walletbackupinterval", 900) * 90; + if (gArgs.IsArgSet("-walletbackupinterval")) { + backup_interval_secs = gArgs.GetArg("-walletbackupinterval", 900) * 90; } return backup_interval_secs; @@ -190,7 +190,7 @@ bool GRC::MaintainBackups(fs::path wallet_backup_path, std::vector // TODO: Probably a good idea to encapsulate it into its own function that can be //used by backups and both loggers. - bool maintain_backup_retention = GetBoolArg("-maintainbackupretention", false); + bool maintain_backup_retention = gArgs.GetBoolArg("-maintainbackupretention", false); // Nothing to do if maintain_backup_retention is not set, which is the default to be // safe (i.e. retain backups indefinitely is the default behavior). @@ -200,19 +200,19 @@ bool GRC::MaintainBackups(fs::path wallet_backup_path, std::vector if (!retention_by_num && !retention_by_days) { // If either argument is set, then assign the one that is set. - if (IsArgSet("-walletbackupretainnumfiles") || IsArgSet("-walletbackupretainnumdays")) + if (gArgs.IsArgSet("-walletbackupretainnumfiles") || gArgs.IsArgSet("-walletbackupretainnumdays")) { // Default to zero for the unset argument, which means unset here. Also, clamp // to zero for nonsensical negative values. That kind of stupidity will be // caught and dealt with below. - retention_by_num = (unsigned int) std::max((int64_t) 0, GetArg("-walletbackupretainnumfiles", 0)); - retention_by_days = (unsigned int) std::max((int64_t) 0, GetArg("-walletbackupretainnumdays", 0)); + retention_by_num = (unsigned int) std::max((int64_t) 0, gArgs.GetArg("-walletbackupretainnumfiles", 0)); + retention_by_days = (unsigned int) std::max((int64_t) 0, gArgs.GetArg("-walletbackupretainnumdays", 0)); } else { // Default to 365 for each. (A very conservative setting.) - retention_by_num = (unsigned int) GetArg("-walletbackupretainnumfiles", 365); - retention_by_days = (unsigned int) GetArg("-walletbackupretainnumdays", 365); + retention_by_num = (unsigned int) gArgs.GetArg("-walletbackupretainnumfiles", 365); + retention_by_days = (unsigned int) gArgs.GetArg("-walletbackupretainnumdays", 365); } } diff --git a/src/gridcoin/boinc.cpp b/src/gridcoin/boinc.cpp index af9ef0fdbf..5741dae77b 100644 --- a/src/gridcoin/boinc.cpp +++ b/src/gridcoin/boinc.cpp @@ -7,7 +7,7 @@ fs::path GRC::GetBoincDataDir() { - std::string path = GetArgument("boincdatadir", ""); + std::string path = gArgs.GetArg("-boincdatadir", ""); if (!path.empty()) { return fs::path(path); diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index 7d519b45ae..929e7324cf 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -146,7 +146,7 @@ void InitializeContracts(CBlockIndex* pindexBest) // If the clearbeaconhistory argument is provided, then clear everything from the beacon registry, // including the beacon_db and beacon key type elements from leveldb. - if (GetBoolArg("-clearbeaconhistory", false)) + if (gArgs.GetBoolArg("-clearbeaconhistory", false)) { beacons.Reset(); } @@ -270,9 +270,9 @@ void ThreadScraperSubscriber(void* parg) void InitializeScraper(ThreadHandlerPtr threads) { // Default to 300 sec (5 min), clamp to 60 minimum, 600 maximum - converted to milliseconds. - nScraperSleep = std::clamp(GetArg("-scrapersleep", 300), 60, 600) * 1000; + nScraperSleep = std::clamp(gArgs.GetArg("-scrapersleep", 300), 60, 600) * 1000; // Default to 14400 sec (4 hrs), clamp to 300 minimum, 86400 maximum (meaning active all of the time). - nActiveBeforeSB = std::clamp(GetArg("-activebeforesb", 14400), 300, 86400); + nActiveBeforeSB = std::clamp(gArgs.GetArg("-activebeforesb", 14400), 300, 86400); // Run the scraper or subscriber housekeeping thread, but not both. The // subscriber housekeeping thread checks if the flag for the scraper thread @@ -283,7 +283,7 @@ void InitializeScraper(ThreadHandlerPtr threads) // For example. gridcoinresearch(d) with no args will run the subscriber // but not the scraper. // gridcoinresearch(d) -scraper will run the scraper but not the subscriber. - if (GetBoolArg("-scraper", false)) { + if (gArgs.GetBoolArg("-scraper", false)) { LogPrintf("Gridcoin: scraper enabled"); if (!threads->createThread(ThreadScraper, nullptr, "ThreadScraper")) { @@ -304,7 +304,7 @@ void InitializeScraper(ThreadHandlerPtr threads) //! void InitializeExplorerFeatures() { - fExplorer = GetBoolArg("-scraper", false) && GetBoolArg("-explorer", false); + fExplorer = gArgs.GetBoolArg("-scraper", false) && gArgs.GetBoolArg("-explorer", false); } //! @@ -390,16 +390,16 @@ void ScheduleUpdateChecks(CScheduler& scheduler) return; } - if (GetBoolArg("-disableupdatecheck", false)) { + if (gArgs.GetBoolArg("-disableupdatecheck", false)) { LogPrintf("Gridcoin: update checks disabled by configuration"); return; } - int64_t hours = GetArg("-updatecheckinterval", 5 * 24); + int64_t hours = gArgs.GetArg("-updatecheckinterval", 5 * 24); if (hours < 1) { LogPrintf("ERROR: invalid -updatecheckinterval: %s. Using default...", - GetArg("-updatecheckinterval", "")); + gArgs.GetArg("-updatecheckinterval", "")); hours = 24; } diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index 4f131115c6..bb979f8182 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -280,7 +280,7 @@ std::optional ReadClientStateXml() LogPrintf("WARNING: Unable to obtain BOINC CPIDs."); - if (!GetArgument("boincdatadir", "").empty()) { + if (!gArgs.GetArg("-boincdatadir", "").empty()) { LogPrintf("Could not access configured BOINC data directory %s", path.string()); } else { LogPrintf( @@ -854,7 +854,7 @@ MiningProject MiningProject::Parse(const std::string& xml) std::strtold(ExtractXML(xml, "", "").c_str(), nullptr)); - if (IsPoolCpid(project.m_cpid) && !GetBoolArg("-pooloperator", false)) { + if (IsPoolCpid(project.m_cpid) && !gArgs.GetBoolArg("-pooloperator", false)) { project.m_error = MiningProject::Error::POOL; return project; } @@ -885,7 +885,7 @@ MiningProject MiningProject::Parse(const std::string& xml) // not reply with an external CPID: // if (IsPoolUsername(ExtractXML(xml, "", "")) - && !GetBoolArg("-pooloperator", false)) + && !gArgs.GetBoolArg("-pooloperator", false)) { project.m_error = MiningProject::Error::POOL; return project; @@ -1141,7 +1141,7 @@ void Researcher::RunRenewBeaconJob() std::string Researcher::Email() { - std::string email = GetArgument("email", ""); + std::string email = gArgs.GetArg("-email", ""); boost::to_lower(email); return email; @@ -1149,7 +1149,7 @@ std::string Researcher::Email() bool Researcher::ConfiguredForInvestorMode(bool log) { - if (GetBoolArg("-investor", false) || Researcher::Email() == "investor") { + if (gArgs.GetBoolArg("-investor", false) || Researcher::Email() == "investor") { if (log) LogPrintf("Investor mode configured. Skipping CPID import."); return true; } @@ -1184,7 +1184,7 @@ void Researcher::Reload() LogPrintf("Loading BOINC CPIDs..."); - if (!GetArgument("boinckey", "").empty()) { + if (!gArgs.GetArg("-boinckey", "").empty()) { // TODO: implement a safer way to export researcher context that does // not risk accidental exposure of an internal CPID and email address. LogPrintf("WARNING: boinckey is no longer supported."); @@ -1214,8 +1214,8 @@ void Researcher::Reload(MiningProjectMap projects, GRC::BeaconError beacon_error // split CPID situation or for people that run a wallet on computers that // do not have BOINC installed: // - if (mapArgs.count("-forcecpid")) { - mining_id = MiningId::Parse(GetArg("-forcecpid", "")); + if (gArgs.IsArgSet("-forcecpid")) { + mining_id = MiningId::Parse(gArgs.GetArg("-forcecpid", "")); if (mining_id.Which() == MiningId::Kind::CPID) { LogPrintf("Configuration forces CPID: %s", mining_id.ToString()); @@ -1401,11 +1401,8 @@ bool Researcher::ChangeMode(const ResearcherMode mode, std::string email) return false; } - { - LOCK(cs_main); - ForceSetArg("-email", email); - ForceSetArg("-investor", mode == ResearcherMode::INVESTOR ? "1" : "0"); - } + gArgs.ForceSetArg("-email", email); + gArgs.ForceSetArg("-investor", mode == ResearcherMode::INVESTOR ? "1" : "0"); Reload(); diff --git a/src/gridcoin/scraper/http.cpp b/src/gridcoin/scraper/http.cpp index a187203560..485be16995 100644 --- a/src/gridcoin/scraper/http.cpp +++ b/src/gridcoin/scraper/http.cpp @@ -270,7 +270,7 @@ std::string Http::GetLatestVersionResponse() { std::string buffer; std::string header; - std::string url = GetArg("-updatecheckurl", "https://api.github.com/repos/gridcoin-community/Gridcoin-Research/releases/latest"); + std::string url = gArgs.GetArg("-updatecheckurl", "https://api.github.com/repos/gridcoin-community/Gridcoin-Research/releases/latest"); struct curl_slist* headers = NULL; headers = curl_slist_append(headers, "Accept: */*"); @@ -301,7 +301,7 @@ std::string Http::GetLatestVersionResponse() void Http::DownloadSnapshot() { - std::string url = GetArg("-snapshoturl", "https://snapshot.gridcoin.us/snapshot.zip"); + std::string url = gArgs.GetArg("-snapshoturl", "https://snapshot.gridcoin.us/snapshot.zip"); fs::path destination = GetDataDir() / "snapshot.zip"; @@ -383,7 +383,7 @@ std::string Http::GetSnapshotSHA256() { std::string buffer; std::string header; - std::string url = GetArg("-snapshotsha256url", "https://snapshot.gridcoin.us/snapshot.zip.sha256"); + std::string url = gArgs.GetArg("-snapshotsha256url", "https://snapshot.gridcoin.us/snapshot.zip.sha256"); struct curl_slist* headers = NULL; headers = curl_slist_append(headers, "Accept: */*"); diff --git a/src/gridcoin/scraper/scraper.cpp b/src/gridcoin/scraper/scraper.cpp index f6a82670ea..e49a671e40 100755 --- a/src/gridcoin/scraper/scraper.cpp +++ b/src/gridcoin/scraper/scraper.cpp @@ -452,7 +452,7 @@ class ScraperLogger bool archive(bool fImmediate, fs::path pfile_out) { - bool fArchiveDaily = GetBoolArg("-logarchivedaily", true); + bool fArchiveDaily = gArgs.GetBoolArg("-logarchivedaily", true); int64_t nTime = GetAdjustedTime(); boost::gregorian::date ArchiveCheckDate = boost::posix_time::from_time_t(nTime).date(); @@ -547,11 +547,11 @@ class ScraperLogger fs::remove(pfile_temp); - bool fDeleteOldLogArchives = GetBoolArg("-deleteoldlogarchives", true); + bool fDeleteOldLogArchives = gArgs.GetBoolArg("-deleteoldlogarchives", true); if (fDeleteOldLogArchives) { - unsigned int nRetention = (unsigned int)GetArg("-logarchiveretainnumfiles", 30); + unsigned int nRetention = (unsigned int)gArgs.GetArg("-logarchiveretainnumfiles", 30); LogPrintf ("INFO: ScraperLogger: nRetention %i.", nRetention); std::set> SortedDirEntries; @@ -3699,7 +3699,7 @@ bool IsScraperAuthorizedToBroadcastManifests(CBitcoinAddress& AddressOut, CKey& _log(logattribute::INFO, "ENDLOCK", "cs_main"); } - std::string sScraperAddressFromConfig = GetArg("-scraperkey", "false"); + std::string sScraperAddressFromConfig = gArgs.GetArg("-scraperkey", "false"); // Check against the -scraperkey config entry first and return quickly to avoid extra work. // If the config entry exists and is in the map (i.e. in the appcache)... diff --git a/src/gridcoin/scraper/scraper_net.cpp b/src/gridcoin/scraper/scraper_net.cpp index ce7b19c84f..9b72d9aa44 100644 --- a/src/gridcoin/scraper/scraper_net.cpp +++ b/src/gridcoin/scraper/scraper_net.cpp @@ -389,7 +389,7 @@ bool CScraperManifest::IsManifestAuthorized(int64_t& nTime, CPubKey& PubKey, uns if (IsScraperMaximumManifestPublishingRateExceeded(nTime, PubKey)) { // Immediate ban - banscore_out = GetArg("-banscore", 100); + banscore_out = gArgs.GetArg("-banscore", 100); return false; } @@ -502,7 +502,7 @@ void CScraperManifest::UnserializeCheck(CDataStream& ss, unsigned int& banscore_ if (!OutOfSyncByAge() && projects.size() > nMaxProjects) { // Immediately ban the node from which the manifest was received. - banscore_out = GetArg("-banscore", 100); + banscore_out = gArgs.GetArg("-banscore", 100); throw error("CScraperManifest::UnserializeCheck: Too many projects in the manifest."); } diff --git a/src/gridcoin/staking/kernel.cpp b/src/gridcoin/staking/kernel.cpp index 8939bec462..4fcf3c37fb 100644 --- a/src/gridcoin/staking/kernel.cpp +++ b/src/gridcoin/staking/kernel.cpp @@ -251,7 +251,7 @@ static bool SelectBlockFromCandidates( } } - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printstakemodifier")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printstakemodifier")) LogPrintf("SelectBlockFromCandidates: selection hash=%s", hashBest.ToString()); return fSelected; @@ -337,12 +337,12 @@ bool GRC::ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nSta nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound); // add the selected block from candidates to selected list mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex)); - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printstakemodifier")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printstakemodifier")) LogPrintf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d", nRound, DateTimeStrFormat(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit()); } // Print selection map for visualization of the selected blocks - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printstakemodifier")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printstakemodifier")) { string strSelectionMap = ""; // '-' indicates proof-of-work blocks not selected diff --git a/src/gridcoin/upgrade.cpp b/src/gridcoin/upgrade.cpp index 6d90e1ee18..5910eaa6da 100644 --- a/src/gridcoin/upgrade.cpp +++ b/src/gridcoin/upgrade.cpp @@ -48,7 +48,7 @@ void Upgrade::ScheduledUpdateCheck() bool Upgrade::CheckForLatestUpdate(std::string& client_message_out, bool ui_dialog, bool snapshotrequest) { // If testnet skip this || If the user changes this to disable while wallet running just drop out of here now. (need a way to remove items from scheduler) - if (fTestNet || (GetBoolArg("-disableupdatecheck", false) && !snapshotrequest)) + if (fTestNet || (gArgs.GetBoolArg("-disableupdatecheck", false) && !snapshotrequest)) return false; Http VersionPull; diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index a317d72efe..c9b7e8bcf9 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -37,6 +37,7 @@ bool AppInit(int argc, char* argv[]) #endif SetupEnvironment(); + SetupServerArgs(); // Note every function above the InitLogging() call must use fprintf or similar. @@ -48,10 +49,13 @@ bool AppInit(int argc, char* argv[]) // // Parameters // - // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() - ParseParameters(argc, argv); - - if (mapArgs.count("-?") || mapArgs.count("-help")) + // If Qt is used, parameters/gridcoinresearch.conf are parsed in qt/bitcoin.cpp's main() + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); + return EXIT_FAILURE; + } + if (HelpRequested(gArgs)) { // First part of help message is specific to bitcoind / RPC client std::string strUsage = _("Gridcoin version") + " " + FormatFullVersion() + "\n\n" + @@ -60,32 +64,52 @@ bool AppInit(int argc, char* argv[]) " gridcoinresearchd [options] [params] " + _("Send command to -server or gridcoinresearchd") + "\n" + " gridcoinresearchd [options] help " + _("List commands") + "\n" + " gridcoinresearchd [options] help " + _("Get help for a command") + "\n"; + strUsage += "\n" + gArgs.GetHelpMessage(); - strUsage += "\n" + HelpMessage(); + tfm::format(std::cout, "%s", strUsage); - fprintf(stdout, "%s", strUsage.c_str()); - return false; + return EXIT_SUCCESS; } - if (mapArgs.count("-version")) + if (gArgs.IsArgSet("-version")) { fprintf(stdout, "%s", VersionMessage().c_str()); return false; } - if (!fs::is_directory(GetDataDir(false))) - { - fprintf(stderr, "Error: Specified directory does not exist\n"); - Shutdown(NULL); + if (!CheckDataDirOption()) { + tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")); + return EXIT_FAILURE; } - /** Check here config file in case TestNet is set there and not in mapArgs **/ + /** Check mainnet config file first in case testnet is set there and not in command line args **/ SelectParams(CBaseChainParams::MAIN); - ReadConfigFile(mapArgs, mapMultiArgs); - SelectParams(mapArgs.count("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); - // Command-line RPC - Test this - ensure single commands execute and exit please. + // Currently unused. + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) + { + tfm::format(std::cerr, "Config file cannot be parsed. Cannot continue.\n"); + exit(1); + } + + SelectParams(gArgs.IsArgSet("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); + + // reread config file after correct chain is selected + if (!gArgs.ReadConfigFiles(error_msg, true)) + { + tfm::format(std::cerr, "Config file cannot be parsed. Cannot continue.\n"); + exit(1); + } + + if (!gArgs.InitSettings(error)) { + tfm::format(std::cerr, "Error initializing settings.\n"); + exit(1); + } + + // Command-line RPC - single commands execute and exit. for (int i = 1; i < argc; i++) if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "gridcoinresearchd")) fCommandLine = true; @@ -100,15 +124,15 @@ bool AppInit(int argc, char* argv[]) InitLogging(); // Make sure a user does not request snapshotdownload and resetblockchaindata at same time! - if (mapArgs.count("-snapshotdownload") && mapArgs.count("-resetblockchaindata")) + if (gArgs.IsArgSet("-snapshotdownload") && gArgs.IsArgSet("-resetblockchaindata")) { - fprintf(stderr, "-snapshotdownload and -resetblockchaindata cannot be used in conjunction"); + tfm::format(std::cerr, "-snapshotdownload and -resetblockchaindata cannot be used in conjunction"); exit(1); } // Check to see if the user requested a snapshot and we are not running TestNet! - if (mapArgs.count("-snapshotdownload") && !mapArgs.count("-testnet")) + if (gArgs.IsArgSet("-snapshotdownload") && !gArgs.IsArgSet("-testnet")) { GRC::Upgrade snapshot; @@ -116,7 +140,8 @@ bool AppInit(int argc, char* argv[]) // Use new probe feature if (!LockDirectory(GetDataDir(), ".lock", false)) { - fprintf(stderr, "Cannot obtain a lock on data directory %s. Gridcoin is probably already running.", GetDataDir().string().c_str()); + tfm::format(std::cerr, "Cannot obtain a lock on data directory %s. Gridcoin is probably already running.", + GetDataDir().string().c_str()); exit(1); } @@ -143,14 +168,14 @@ bool AppInit(int argc, char* argv[]) } // Check to see if the user requested to reset blockchain data -- We allow reset blockchain data on testnet, but not a snapshot download. - if (mapArgs.count("-resetblockchaindata")) + if (gArgs.IsArgSet("-resetblockchaindata")) { GRC::Upgrade resetblockchain; // Let's check make sure gridcoin is not already running in the data directory. if (!LockDirectory(GetDataDir(), ".lock", false)) { - fprintf(stderr, "Cannot obtain a lock on data directory %s. Gridcoin is probably already running.", GetDataDir().string().c_str()); + tfm::format(std::cerr, "Cannot obtain a lock on data directory %s. Gridcoin is probably already running.", GetDataDir().string().c_str()); exit(1); } @@ -166,7 +191,7 @@ bool AppInit(int argc, char* argv[]) std::string inftext = resetblockchain.ResetBlockchainMessages(resetblockchain.CleanUp); - fprintf(stderr, "%s", inftext.c_str()); + tfm::format(std::cerr, "%s", inftext.c_str()); exit(1); } diff --git a/src/init.cpp b/src/init.cpp index 16c03f4309..3a078484a1 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -15,6 +15,7 @@ #include "ui_interface.h" #include "scheduler.h" #include "gridcoin/gridcoin.h" +#include "miner.h" #include #include @@ -26,7 +27,6 @@ static boost::thread_group threadGroup; static CScheduler scheduler; extern void ThreadAppInit2(void* parg); -bool IsConfigFileEmpty(); #ifndef WIN32 #include @@ -49,6 +49,16 @@ static constexpr int DUMP_BANS_INTERVAL = 300; std::unique_ptr g_banman; +/** + * The PID file facilities. + */ +static const char* GRIDCOIN_PID_FILENAME = "gridcoinresearchd.pid"; + +static fs::path GetPidFile(const ArgsManager& args) +{ + return AbsPathForConfigVal(fs::path(args.GetArg("-pid", GRIDCOIN_PID_FILENAME))); +} + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -109,7 +119,7 @@ void Shutdown(void* parg) // step because of a write lock on accrual/registry.dat. GRC::CloseResearcherRegistryFile(); - fs::remove(GetPidFile()); + fs::remove(GetPidFile(gArgs)); UnregisterWallet(pwalletMain); delete pwalletMain; // close transaction database to prevent lock issue on restart @@ -197,120 +207,349 @@ static void CreateNewConfigFile() << "addnode=www.grcpool.com\n"; } -// Core-specific options shared between UI and daemon -std::string HelpMessage() +void AddLoggingArgs(ArgsManager& argsman) +{ + argsman.AddArg("-debuglogfile=", strprintf("Specify location of debug log file. Relative paths will be prefixed " + "by a net-specific datadir location. (-nodebuglogfile to disable; " + "default: %s)", + DEFAULT_DEBUGLOGFILE), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-debug=", "Output debugging information (default: -nodebug, supplying is optional). " + "If is not supplied or if = 1, output all debugging information. can be: " + + ListLogCategories() + ". This option can be specified multiple times to output multiple categories.", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-debugexclude=", strprintf("Exclude debugging information for a category. Can be used in" + " conjunction with -debug=1 to output debug logs for all categories" + " except the specified category. This option can be specified" + " multiple times to exclude multiple categories."), + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); +#ifdef HAVE_THREAD_LOCAL + argsman.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on" + " platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); +#else + argsman.AddHiddenArgs({"-logthreadnames"}); +#endif + argsman.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", + DEFAULT_LOGTIMEMICROS), + ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 0) )", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-printtodebugger", "Send trace/debug info to debugger (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-logarchivedaily", "Archive log file to compressed archive daily (default: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-deleteoldlogarchives", "Delete oldest log archive files in excess of -logarchiveretainnumfiles " + "setting", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-logarchiveretainnumfiles=", "Specify number of compressed log archive files to retain" + " (default: 30)", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-org=", "Set organization name for identification (default: not set). Required for use " + "on testnet. Do not set this for a wallet on mainnet.", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); +} + +void SetupServerArgs() { - //gridcoinresearch ports: testnet ? 32748 : 32749; - string strUsage = _("Options:") + "\n" + - " -? " + _("This help message") + "\n" + - " -version " + _("Print version and exit") + "\n" + - " -conf= " + _("Specify configuration file (default: gridcoinresearch.conf)") + "\n" + - " -pid= " + _("Specify pid file (default: gridcoind.pid)") + "\n" + - " -datadir= " + _("Specify data directory") + "\n" + - " -wallet= " + _("Specify wallet file (within data directory)") + "\n" + - " -dbcache= " + _("Set database cache size in megabytes (default: 25)") + "\n" + - " -dblogsize= " + _("Set database disk log size in megabytes (default: 100)") + "\n" + - " -timeout= " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + - " -peertimeout= " + _("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: 45)") + "\n" - " -proxy= " + _("Connect through socks proxy") + "\n" + - " -socks= " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + - " -tor= " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n" - " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + - " -port= " + _("Listen for connections on (default: 32749 or testnet: 32748)") + "\n" + - " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + - " -maxoutboundconnections="+ _("Maximum number of outbound connections (default: 8)") + "\n" + - " -addnode= " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + - " -connect= " + _("Connect only to the specified node(s)") + "\n" + - " -seednode= " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + - " -externalip= " + _("Specify your own public address") + "\n" + - " -onlynet= " + _("Only connect to nodes in network (IPv4, IPv6 or Tor)") + "\n" + - " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + - " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + - " -bind= " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + - " -dnsseed " + _("Find peers using DNS lookup (default: 1)") + "\n" + - " -synctime " + _("Sync time with other nodes. Disable if time on your system is precise e.g. syncing with NTP (default: 1)") + "\n" + - " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + - " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + - " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 5000)") + "\n" + - " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 1000)") + "\n" + + ArgsManager& argsman = gArgs; + + SetupHelpOptions(argsman); + argsman.AddArg("-help-debug", "Print help message with debugging options and exit", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + + AddLoggingArgs(argsman); + + const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); + const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); + const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); + const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); + + // Hidden Options + std::vector hidden_args = { + "-dbcrashratio", "-forcecompactdb", "-fastindex", + // GUI args. These will be overwritten by SetupUIArgs for the GUI + "-choosedatadir", "-lang=", "-min", "-resetguisettings", + "-splash", "-style", "-suppressnetworkgraph", "-showorphans"}; + + // Listed Options + // General + argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + // TODO: Read-only config file? + argsman.AddArg("-conf=", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed" + " by datadir location. (default: %s)", GRIDCOIN_CONF_FILENAME), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-pid=", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir" + " location. (default: %s)", GRIDCOIN_PID_FILENAME), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-wallet=", "Specify wallet file (within data directory)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-dbcache=", "Set database cache size in megabytes (default: 25)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-dblogsize=", "Set database disk log size in megabytes (default: 100)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-synctime", "Sync time with other nodes. Disable if time on your system is precise e.g. syncing with" + " NTP (default: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-paytxfee=", "Fee per KB to add to transactions you send", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-mintxfee=", "Minimum transaction fee for transactions you send or process (default: 0.001 GRC)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-mininput=", "When creating transactions, ignore inputs with value less than this (default: 0.01)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-daemon", "Run in the background as a daemon and accept commands", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-testnet", "Use the test network", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-blocknotify=", "Execute command when the best block changes (%s in cmd is replaced by block hash)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-walletnotify=", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-confchange", "Require confirmations for change (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-enforcecanonical", "Enforce transaction scripts to use canonical PUSH operators (default: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-alertnotify=", "Execute command when a relevant alert is received or we see a really long fork" + " (%s in cmd is replaced by message)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-blockminsize=", "Set minimum block size in bytes (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + // TODO: Check the maximize block size default. + argsman.AddArg("-blockmaxsize=", strprintf("Set maximum block size in bytes (default: %u)", MAX_BLOCK_SIZE_GEN/2), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-blockprioritysize=", "Set maximum size of high-priority/low-fee transactions in bytes" + " (default: 27000)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-snapshotdownload", "Download and apply latest snapshot", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-snapshoturl=", "Optional: URL for the snapshot.zip file (ex: " + "https://sub.domain.com/location/snapshot.zip)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-snapshotsha256url=", "Optional: URL for the snapshot.sha256 file (ex: " + "https://sub.domain.com/location/snapshot.sha256)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-disableupdatecheck", "Optional: Disable update checks by wallet", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-updatecheckinterval=", "Optional: Check for updates every hours (default: 120, minimum: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-updatecheckurl=", "Optional: URL for the update version checks (ex: " + "https://sub.domain.com/location/latest", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-resetblockchaindata", "Reset blockchain data. This argument will remove all previous blockchain data", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-loadblock=", "Imports blocks from external file on startup", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + //TODO: Implement reindex option + //argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", + // ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-settings=", strprintf("Specify path to dynamic settings data file. Can be disabled with" + " -nosettings. File is written at runtime and not meant to be edited by" + " users (use %s instead for custom settings). Relative paths will be" + " prefixed by datadir location. (default: %s)", + GRIDCOIN_CONF_FILENAME, GRIDCOIN_SETTINGS_FILENAME), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + //argsman.AddArg("-startupnotify=", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-enablesidestaking", "Enable side staking functionality (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-staking", "Allow wallet to stake if conditions to stake are met (default: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-sidestake=", "Sidestake destination and allocation entry. There can be as many " + "specified as desired. Only six per stake can be sent. If more than " + "six are specified. Six are randomly chosen for each stake. Only active " + "if -enablesidestaking is set.", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-enablestakesplit", "Enable unspent output spitting when staking to optimize staking efficiency " + "(default: 0", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-stakingefficiency=", "Specify target staking efficiency for stake splitting (default: 90, " + "clamped to [75, 98])", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-minstakesplitvalue=", strprintf("Specify minimum output value for post split output when stake " + "splitting (default: %" PRId64 "GRC)", MIN_STAKE_SPLIT_VALUE_GRC), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + + // Scraper + argsman.AddArg("-scraper", "Activate scraper for statistics downloads. This will only work if the node has a wallet " + "key that is authorized by the network (default: 0).", + ArgsManager::ALLOW_ANY, OptionsCategory::SCRAPER); + argsman.AddArg("-explorer", "Activate extended statistics file retention for the scraper. This will only work if the" + "node is authorized for scraping and the scraper is activated (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::SCRAPER); + argsman.AddArg("-scraperkey=
", "Manually specify scraper public key in address form. This is not necessary " + "and will not work if the private key is not present in the scraper wallet file.", + ArgsManager::ALLOW_ANY, OptionsCategory::SCRAPER); + + // Researcher + argsman.AddArg("-email=", "Email address to use for CPID detection. Must match your BOINC account email", + ArgsManager::ALLOW_ANY, OptionsCategory::RESEARCHER); + argsman.AddArg("-boincdatadir=", "Path to the BOINC data directory for CPID detection when the BOINC client uses" + " a non-default directory", + ArgsManager::ALLOW_ANY, OptionsCategory::RESEARCHER); + argsman.AddArg("-forcecpid=", "Override automatic CPID detection with the specified CPID", + ArgsManager::ALLOW_ANY, OptionsCategory::RESEARCHER); + argsman.AddArg("-investor", "Disable CPID detection and do not participate in the research reward system", + ArgsManager::ALLOW_ANY, OptionsCategory::RESEARCHER); + argsman.AddArg("-pooloperator", "Skip pool CPID checks for staking nodes run by pool administrators", + ArgsManager::ALLOW_ANY, OptionsCategory::RESEARCHER); + + // Wallet + argsman.AddArg("-upgradewallet", "Upgrade wallet to latest format", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-keypool=", "Set key pool size to (default: 100)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-rescan", "Rescan the block chain for missing wallet transactions", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet.dat", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-zapwallettxes", "Delete all wallet transactions and only recover those parts of the blockchain through" + " -rescan on startup", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-checkblocks=", "How many blocks to check at startup (default: 2500, 0 = all)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-checklevel=", "How thorough the block verification is (0-6, default: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-walletbackupinterval=", "DEPRECATED: Optional: Create a wallet backup every blocks. Zero" + " disables backups", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-walletbackupintervalsecs=", "Optional: Create a wallet backup every seconds. Zero disables" + " backups (default: 86400)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-backupdir=", "Specify backup directory for wallet backups (default: walletbackups).", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-maintainbackupretention", "Activate retention management of backup files (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-walletbackupretainnumfiles=", "Specify maximum number of backup files to retain (default: 365). " + "Note that the actual files retained is the greater of this setting " + "and the other setting -walletbackupretainnumdays.", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-walletbackupretainnumdays=", "Specify maximum number of backup files to retain (default: 365). " + "Note that the actual files retained is the greater of this setting " + "and the other setting -walletbackupretainnumfiles.", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-enableaccounts", "DEPRECATED: Enable accounting functionality (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-maxsigcachesize=", "Set maximum size for signature cache (default: 50000)", + ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + + + // Connections + argsman.AddArg("-timeout=", "Specify connection timeout in milliseconds (default: 5000)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-peertimeout=", "Specify p2p connection timeout in seconds. This option determines the amount of time" + " a peer may be inactive before the connection to it is dropped. (minimum: 1, default:" + " 45)", + ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-proxy=", "Connect through socks proxy", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-socks=", "Select the version of socks proxy to use (4-5, default: 5)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-tor=", "Use proxy to reach tor hidden services (default: same as -proxy)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-dns", "Allow DNS lookups for -addnode, -seednode and -connect", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-port=", "Listen for connections on (default: 32749 or testnet: 32748)", + ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-maxconnections=", "Maintain at most connections to peers (default: 125)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-maxoutboundconnections=", "Maximum number of outbound connections (default: 8)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-addnode=", "Add a node to connect to and attempt to keep the connection open", + ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-connect=", "Connect only to the specified node(s)", + ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-seednode=", "Connect to a node to retrieve peer addresses, and disconnect", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-externalip=", "Specify your own public address", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-onlynet=", "Only connect to nodes in network (IPv4, IPv6 or Tor)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-discover", "Discover own IP address (default: 1 when listening and no -externalip)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-bind=[:][=onion]", "Bind to given address. Use [host]:port notation for IPv6", + ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-dnsseed", "Find peers using DNS lookup (default: 1)", + ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION); + argsman.AddArg("-banscore=", "Threshold for disconnecting misbehaving peers (default: 100)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-bantime=", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", + DEFAULT_MISBEHAVING_BANTIME), + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-maxreceivebuffer=", "Maximum per-connection receive buffer, *1000 bytes (default: 5000)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-maxsendbuffer=", "Maximum per-connection send buffer, *1000 bytes (default: 1000)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); #ifdef USE_UPNP #if USE_UPNP - " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n" + + argsman.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); #else - " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + + argsman.AddArg("-upnp", "Use UPnP to map the listening port (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); #endif +#else + hidden_args.emplace_back("-upnp"); #endif - " -paytxfee= " + _("Fee per KB to add to transactions you send") + "\n" + - " -mininput= " + _("When creating transactions, ignore inputs with value less than this (default: 0.01)") + "\n"; - if(fQtActive) - strUsage += - " -server " + _("Accept command line and JSON-RPC commands") + "\n" + - " -showorphans " + _("Include stale (orphaned) coinstake transactions in the transaction list") + "\n"; -#if !defined(WIN32) - if(!fQtActive) - strUsage += - " -daemon " + _("Run in the background as a daemon and accept commands") + "\n"; -#endif - strUsage += - " -testnet " + _("Use the test network") + "\n" + - " -debug " + _("Output extra debugging information.") + "\n" + - " -logtimestamps " + _("Prepend debug output with timestamp") + "\n" + - " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n" + - " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n" + -#ifdef WIN32 - " -printtodebugger " + _("Send trace/debug info to debugger") + "\n" + -#endif - " -rpcuser= " + _("Username for JSON-RPC connections") + "\n" + - " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n" + - " -rpcport= " + _("Listen for JSON-RPC connections on (default: 15715 or testnet: 25715)") + "\n" + - " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + - " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + - " -rpcthreads= " + _("Set the number of threads to service RPC calls (default: 4)") + "\n" + - " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + - " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + - " -confchange " + _("Require a confirmations for change (default: 0)") + "\n" + - " -enforcecanonical " + _("Enforce transaction scripts to use canonical PUSH operators (default: 1)") + "\n" + - " -alertnotify= " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" + - " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + - " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + - " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + - " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + - " -zapwallettxes " + _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + "\n" + - " -checkblocks= " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + - " -checklevel= " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + - " -loadblock= " + _("Imports blocks from external blk000?.dat file") + "\n" + - - " -walletbackupinterval= " + _("DEPRECATED: Optional: Create a wallet backup every blocks. Zero disables backups") + "\n" - " -walletbackupintervalsecs= " + _("Optional: Create a wallet backup every seconds. Zero disables backups (default: 86400)") + "\n" - - "\n" + _("Block creation options:") + "\n" + - " -blockminsize= " + _("Set minimum block size in bytes (default: 0)") + "\n" + - " -blockmaxsize= " + _("Set maximum block size in bytes (default: 250000)") + "\n" + - " -blockprioritysize= " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n" + - - "\n" + _("Research reward system options:") + "\n" + - " -email= " + _("Email address to use for CPID detection. Must match your BOINC account email") + "\n" + - " -boincdatadir= " + _("Path to the BOINC data directory for CPID detection when the BOINC client uses a non-default directory") + "\n" + - " -forcecpid= " + _("Override automatic CPID detection with the specified CPID") + "\n" + - " -investor " + _("Disable CPID detection and do not participate in the research reward system") + "\n" + - " -pooloperator " + _("Skip pool CPID checks for staking nodes run by pool administrators") + "\n" + - - "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n" + - " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + - " -rpcsslcertificatechainfile= " + _("Server certificate file (default: server.cert)") + "\n" + - " -rpcsslprivatekeyfile= " + _("Server private key (default: server.pem)") + "\n" + - " -rpcsslciphers= " + _("Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)") + "\n" - - "\n" + _("Update/Snapshot options:") + "\n" - " -snapshotdownload " + _("Download and apply latest snapshot") + "\n" - " -snapshoturl= " + _("Optional: URL for the snapshot.zip file (ex: https://sub.domain.com/location/snapshot.zip)") + "\n" - " -snapshotsha256url= " + _("Optional: URL for the snapshot.sha256 file (ex: https://sub.domain.com/location/snapshot.sha256)") + "\n" - " -disableupdatecheck " + _("Optional: Disable update checks by wallet") + "\n" - " -updatecheckinterval= " + _("Optional: Check for updates every hours (default: 120, minimum: 1)") + "\n" - " -updatecheckurl= " + _("Optional: URL for the update version checks (ex: https://sub.domain.com/location/latest") + "\n" - " -resetblockchaindata " + _("Reset blockchain data. This argument will remove all previous blockchain data") + "\n"; - - return strUsage; + + // RPC + argsman.AddArg("-server", "Accept command line and JSON-RPC commands", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", + ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); + argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", + ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); + argsman.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on (default: %u, testnet: %u)", + defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcallowip=", "Allow JSON-RPC connections from specified IP address", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcconnect=", "Send commands to node running on (default: 127.0.0.1)", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcthreads=", "Set the number of threads to service RPC calls (default: 4)", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcssl", "Use OpenSSL (https) for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcsslcertificatechainfile=", "Server certificate file (default: server.cert)", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcsslprivatekeyfile=", "Server private key (default: server.pem)", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcsslciphers=", + "Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)", + ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + + hidden_args.emplace_back("-devbuild"); + hidden_args.emplace_back("-scrapersleep"); + hidden_args.emplace_back("-activebeforesb"); + hidden_args.emplace_back("-clearbeaconhistory"); + + // -boinckey should now be removed entirely. It is put here to prevent the executable erroring out on + // an invalid parameter for old clients that may have left the argument in. + hidden_args.emplace_back("-boinckey"); + + hidden_args.emplace_back("-printstakemodifier"); + hidden_args.emplace_back("-printpriority"); + hidden_args.emplace_back("-printkeypool"); + + // -limitfreerelay is probably destined for the trash-heap on the next fee rewrite. + hidden_args.emplace_back("-limitfreerelay"); + + // Rob? + hidden_args.emplace_back("-autoban"); + + // These probably should be removed + hidden_args.emplace_back("-printcoinage"); + hidden_args.emplace_back("-privdb"); + + // This is hidden because it defaults to true and should NEVER be changed unless you know what you are doing. + hidden_args.emplace_back("-flushwallet"); + + SetupChainParamsBaseOptions(argsman); + + // Add the hidden options + argsman.AddHiddenArgs(hidden_args); } std::string VersionMessage() @@ -347,23 +586,23 @@ bool InitSanityCheck(void) */ void InitLogging() { - fPrintToConsole = GetBoolArg("-printtoconsole"); - fPrintToDebugger = GetBoolArg("-printtodebugger"); - fLogTimestamps = GetBoolArg("-logtimestamps", true); + fPrintToConsole = gArgs.GetBoolArg("-printtoconsole"); + fPrintToDebugger = gArgs.GetBoolArg("-printtodebugger"); + fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", true); - LogInstance().m_print_to_file = !IsArgNegated("-debuglogfile"); - LogInstance().m_file_path = AbsPathForConfigVal(GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile"); + LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); LogInstance().m_print_to_console = fPrintToConsole; LogInstance().m_log_timestamps = fLogTimestamps; - LogInstance().m_log_time_micros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); - LogInstance().m_log_threadnames = GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES); + LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES); - fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS); + fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); if (LogInstance().m_print_to_file) { // Only shrink debug file at start if log archiving is set to false. - if (!GetBoolArg("-logarchivedaily", true) && GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) + if (!gArgs.GetBoolArg("-logarchivedaily", true) && gArgs.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) { // Do this first since it both loads a bunch of debug.log into memory, // and because this needs to happen before any other debug.log printing @@ -371,14 +610,14 @@ void InitLogging() } } - if (IsArgSet("-debug")) + if (gArgs.IsArgSet("-debug")) { // Special-case: if -debug=0/-nodebug is set, turn off debugging messages std::vector categories; - if (mapArgs.count("-debug") && mapMultiArgs["-debug"].size() > 0) + if (gArgs.GetArgs("-debug").size()) { - for (auto const& sSubParam : mapMultiArgs["-debug"]) + for (auto const& sSubParam : gArgs.GetArgs("-debug")) { categories.push_back(sSubParam); } @@ -399,9 +638,9 @@ void InitLogging() std::vector excluded_categories; - if (mapArgs.count("-debugexclude") && mapMultiArgs["-debugexclude"].size() > 0) + if (gArgs.GetArgs("-debugexclude").size()) { - for (auto const& sSubParam : mapMultiArgs["-debugexclude"]) + for (auto const& sSubParam : gArgs.GetArgs("-debugexclude")) { excluded_categories.push_back(sSubParam); } @@ -516,7 +755,13 @@ bool AppInit2(ThreadHandlerPtr threads) try { CreateNewConfigFile(); - ReadConfigFile(mapArgs, mapMultiArgs); + + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) + { + throw error_msg; + } } catch (const std::exception& e) { @@ -536,60 +781,60 @@ bool AppInit2(ThreadHandlerPtr threads) LogPrintf("Boost Version: %s", s.str()); - nNodeLifespan = GetArg("-addrlifespan", 7); - fUseFastIndex = GetBoolArg("-fastindex", false); + nNodeLifespan = gArgs.GetArg("-addrlifespan", 7); + fUseFastIndex = gArgs.GetBoolArg("-fastindex", false); - nMinerSleep = GetArg("-minersleep", 8000); + nMinerSleep = gArgs.GetArg("-minersleep", 8000); nDerivationMethodIndex = 0; - fTestNet = GetBoolArg("-testnet"); + fTestNet = gArgs.GetBoolArg("-testnet"); - if (mapArgs.count("-bind")) { + if (gArgs.GetArgs("-bind").size()) { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified - SoftSetBoolArg("-listen", true); + gArgs.SoftSetBoolArg("-listen", true); } - if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { + if (gArgs.GetArgs("-connect").size()) { // when only connecting to trusted nodes, do not seed via DNS, or listen by default - SoftSetBoolArg("-dnsseed", false); - SoftSetBoolArg("-listen", false); + gArgs.SoftSetBoolArg("-dnsseed", false); + gArgs.SoftSetBoolArg("-listen", false); } - if (mapArgs.count("-proxy")) { + if (gArgs.GetArgs("-proxy").size()) { // to protect privacy, do not listen by default if a proxy server is specified - SoftSetBoolArg("-listen", false); + gArgs.SoftSetBoolArg("-listen", false); } - if (!GetBoolArg("-listen", true)) { + if (!gArgs.GetBoolArg("-listen", true)) { // do not map ports or try to retrieve public IP when not listening (pointless) - SoftSetBoolArg("-upnp", false); - SoftSetBoolArg("-discover", false); + gArgs.SoftSetBoolArg("-upnp", false); + gArgs.SoftSetBoolArg("-discover", false); } - if (mapArgs.count("-externalip")) { + if (gArgs.GetArgs("-externalip").size()) { // if an explicit public IP is specified, do not try to find others - SoftSetBoolArg("-discover", false); + gArgs.SoftSetBoolArg("-discover", false); } - if (GetBoolArg("-salvagewallet")) { + if (gArgs.GetBoolArg("-salvagewallet")) { // Rewrite just private keys: rescan to find transactions - SoftSetBoolArg("-rescan", true); + gArgs.SoftSetBoolArg("-rescan", true); } - if (GetBoolArg("-zapwallettxes", false)) { + if (gArgs.GetBoolArg("-zapwallettxes", false)) { // -zapwallettx implies a rescan - SoftSetBoolArg("-rescan", true); + gArgs.SoftSetBoolArg("-rescan", true); } // Verify testnet is using the testnet directory for the config file: - std::string sTestNetSpecificArg = GetArgument("testnetarg","default"); - LogPrintf("Using specific arg %s",sTestNetSpecificArg); + std::string sTestNetSpecificArg = gArgs.GetArg("-testnetarg", "default"); + LogPrintf("Using specific arg %s", sTestNetSpecificArg); // ********************************************************* Step 3: parameter-to-internal-flags - if (GetArg("-debug", "false") == "true") + if (gArgs.GetArg("-debug", "false") == "true") { LogPrintf("Enabling debug category VERBOSE from legacy debug."); LogInstance().EnableCategory(BCLog::LogFlags::VERBOSE); @@ -601,50 +846,50 @@ bool AppInit2(ThreadHandlerPtr threads) if(fQtActive) fDaemon = false; else - fDaemon = GetBoolArg("-daemon"); + fDaemon = gArgs.GetBoolArg("-daemon"); #endif if (fDaemon) fServer = true; else - fServer = GetBoolArg("-server"); + fServer = gArgs.GetBoolArg("-server"); /* force fServer when running without GUI */ if(!fQtActive) fServer = true; - if (mapArgs.count("-timeout")) + if (gArgs.IsArgSet("-timeout")) { - int nNewTimeout = GetArg("-timeout", 5000); + int nNewTimeout = gArgs.GetArg("-timeout", 5000); if (nNewTimeout > 0 && nNewTimeout < 600000) nConnectTimeout = nNewTimeout; } - if (mapArgs.count("-peertimeout")) + if (gArgs.IsArgSet("-peertimeout")) { - int nNewPeerTimeout = GetArg("-peertimeout", 45); + int nNewPeerTimeout = gArgs.GetArg("-peertimeout", 45); if (nNewPeerTimeout <= 0) - InitError(strprintf(_("Invalid amount for -peertimeout=: '%s'"), mapArgs["-peertimeout"])); + InitError(strprintf(_("Invalid amount for -peertimeout=: '%s'"), gArgs.GetArg("-peertimeout", ""))); PEER_TIMEOUT = nNewPeerTimeout; } - if (mapArgs.count("-paytxfee")) + if (gArgs.IsArgSet("-paytxfee")) { - if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) - return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"])); + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nTransactionFee)) + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), gArgs.GetArg("-paytxfee", ""))); if (nTransactionFee > 0.25 * COIN) InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); } - fConfChange = GetBoolArg("-confchange", false); - fEnforceCanonical = GetBoolArg("-enforcecanonical", true); + fConfChange = gArgs.GetBoolArg("-confchange", false); + fEnforceCanonical = gArgs.GetBoolArg("-enforcecanonical", true); - if (mapArgs.count("-mininput")) + if (gArgs.IsArgSet("-mininput")) { - if (!ParseMoney(mapArgs["-mininput"], nMinimumInputValue)) - return InitError(strprintf(_("Invalid amount for -mininput=: '%s'"), mapArgs["-mininput"])); + if (!ParseMoney(gArgs.GetArg("-mininput", ""), nMinimumInputValue)) + return InitError(strprintf(_("Invalid amount for -mininput=: '%s'"), gArgs.GetArg("-mininput", ""))); } // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log @@ -659,7 +904,9 @@ bool AppInit2(ThreadHandlerPtr threads) LogPrintf("Block version 11 hard fork configured for block %d", Params().GetConsensus().BlockV11Height); fs::path datadir = GetDataDir(); - fs::path walletFileName = GetArg("-wallet", "wallet.dat"); + fs::path walletFileName = gArgs.GetArg("-wallet", "wallet.dat"); + + LogPrintf("INFO %s: DataDir = %s.", __func__, datadir.string()); // WalletFileName must be a plain filename without a directory if (walletFileName != walletFileName.filename()) @@ -686,7 +933,7 @@ bool AppInit2(ThreadHandlerPtr threads) } if (pid > 0) { - CreatePidFile(GetPidFile(), pid); + CreatePidFile(GetPidFile(gArgs), pid); // Now that we are forked we can request a shutdown so the parent // exits while the child lives on. @@ -708,7 +955,7 @@ bool AppInit2(ThreadHandlerPtr threads) if ((CLIENT_VERSION_BUILD != 0) && !fTestNet) { fDevbuildCripple = true; - if ((GetArg("-devbuild", "") == "override")) + if ((gArgs.GetArg("-devbuild", "") == "override")) { LogInstance().EnableCategory(BCLog::LogFlags::VERBOSE); fDevbuildCripple = false; @@ -741,7 +988,7 @@ bool AppInit2(ThreadHandlerPtr threads) } - if (GetBoolArg("-salvagewallet")) + if (gArgs.GetBoolArg("-salvagewallet")) { // Recover readable key pairs: if (!CWalletDB::Recover(bitdb, walletFileName.string(), true)) @@ -768,14 +1015,14 @@ bool AppInit2(ThreadHandlerPtr threads) // ********************************************************* Step 6: network initialization - int nSocksVersion = GetArg("-socks", 5); + int nSocksVersion = gArgs.GetArg("-socks", 5); if (nSocksVersion != 4 && nSocksVersion != 5) return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion)); - if (mapArgs.count("-onlynet")) { + if (gArgs.GetArgs("-onlynet").size()) { std::set nets; - for (auto const& snet : mapMultiArgs["-onlynet"]) + for (auto const& snet : gArgs.GetArgs("-onlynet")) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) @@ -791,10 +1038,10 @@ bool AppInit2(ThreadHandlerPtr threads) CService addrProxy; bool fProxy = false; - if (mapArgs.count("-proxy")) { - addrProxy = CService(mapArgs["-proxy"], 9050); + if (gArgs.IsArgSet("-proxy")) { + addrProxy = CService(gArgs.GetArg("-proxy", ""), 9050); if (!addrProxy.IsValid()) - return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"])); + return InitError(strprintf(_("Invalid -proxy address: '%s'"), gArgs.GetArg("-proxy", ""))); if (!IsLimited(NET_IPV4)) SetProxy(NET_IPV4, addrProxy, nSocksVersion); @@ -807,32 +1054,32 @@ bool AppInit2(ThreadHandlerPtr threads) } // -tor can override normal proxy, -notor disables tor entirely - if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { + if (gArgs.IsArgSet("-tor") && (fProxy || gArgs.IsArgSet("-tor"))) { CService addrOnion; - if (!mapArgs.count("-tor")) + if (!gArgs.IsArgSet("-tor")) addrOnion = addrProxy; else - addrOnion = CService(mapArgs["-tor"], 9050); + addrOnion = CService(gArgs.GetArg("-tor", ""), 9050); if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"])); + return InitError(strprintf(_("Invalid -tor address: '%s'"), gArgs.GetArg("-tor", ""))); SetProxy(NET_TOR, addrOnion, 5); SetReachable(NET_TOR, true); } // see Step 2: parameter interactions for more information about these - fNoListen = !GetBoolArg("-listen", true); - fDiscover = GetBoolArg("-discover", true); - fNameLookup = GetBoolArg("-dns", true); + fNoListen = !gArgs.GetBoolArg("-listen", true); + fDiscover = gArgs.GetBoolArg("-discover", true); + fNameLookup = gArgs.GetBoolArg("-dns", true); #ifdef USE_UPNP - fUseUPnP = GetBoolArg("-upnp", USE_UPNP); + fUseUPnP = gArgs.GetBoolArg("-upnp", USE_UPNP); #endif bool fBound = false; if (!fNoListen) { std::string strError; - if (mapArgs.count("-bind")) { - for (auto const& strBind : mapMultiArgs["-bind"]) + if (gArgs.GetArgs("-bind").size()) { + for (auto const& strBind : gArgs.GetArgs("-bind")) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) @@ -851,9 +1098,9 @@ bool AppInit2(ThreadHandlerPtr threads) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } - if (mapArgs.count("-externalip")) + if (gArgs.GetArgs("-externalip").size()) { - for (auto const& strAddr : mapMultiArgs["-externalip"]) + for (auto const& strAddr : gArgs.GetArgs("-externalip")) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) @@ -862,17 +1109,19 @@ bool AppInit2(ThreadHandlerPtr threads) } } - if (mapArgs.count("-reservebalance")) // ppcoin: reserve balance amount + if (gArgs.IsArgSet("-reservebalance")) // ppcoin: reserve balance amount { - if (!ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + if (!ParseMoney(gArgs.GetArg("-reservebalance", ""), nReserveBalance)) { InitError(_("Invalid amount for -reservebalance=")); return false; } } - for (auto const& strDest : mapMultiArgs["-seednode"]) + for (auto const& strDest : gArgs.GetArgs("-seednode")) + { AddOneShot(strDest); + } g_timer.GetTimes("Finished initializing network", "init"); @@ -886,7 +1135,7 @@ bool AppInit2(ThreadHandlerPtr threads) return InitError(msg); } - if (GetBoolArg("-loadblockindextest")) + if (gArgs.GetBoolArg("-loadblockindextest")) { CTxDB txdb("r"); txdb.LoadBlockIndex(); @@ -910,15 +1159,15 @@ bool AppInit2(ThreadHandlerPtr threads) g_timer.GetTimes("Finished loading block chain", "init"); - if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) + if (gArgs.GetBoolArg("-printblockindex") || gArgs.GetBoolArg("-printblocktree")) { PrintBlockTree(); return false; } - if (mapArgs.count("-printblock")) + if (gArgs.IsArgSet("-printblock")) { - string strMatch = mapArgs["-printblock"]; + string strMatch = gArgs.GetArg("-printblock", ""); int nFound = 0; for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { @@ -967,9 +1216,9 @@ bool AppInit2(ThreadHandlerPtr threads) strErrors << _("Error loading wallet.dat") << "\n"; } - if (GetBoolArg("-upgradewallet", fFirstRun)) + if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { - int nMaxVersion = GetArg("-upgradewallet", 0); + int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); if (nMaxVersion == 0) // the -upgradewallet without argument case { LogPrintf("Performing wallet upgrade to %i", FEATURE_LATEST); @@ -1001,7 +1250,7 @@ bool AppInit2(ThreadHandlerPtr threads) g_timer.GetTimes("Finished loading wallet file", "init"); // Zap wallet transactions if specified as a command line argument. - if (GetBoolArg("-zapwallettxes", false)) + if (gArgs.GetBoolArg("-zapwallettxes", false)) { std::vector vWtx; @@ -1020,7 +1269,7 @@ bool AppInit2(ThreadHandlerPtr threads) RegisterWallet(pwalletMain); CBlockIndex *pindexRescan = pindexBest; - if (GetBoolArg("-rescan")) + if (gArgs.GetBoolArg("-rescan")) pindexRescan = pindexGenesisBlock; else { @@ -1043,11 +1292,11 @@ bool AppInit2(ThreadHandlerPtr threads) // ********************************************************* Step 9: import blocks - if (mapArgs.count("-loadblock")) + if (gArgs.GetArgs("-loadblock").size()) { uiInterface.InitMessage(_("Importing blockchain data file.")); - for (auto const& strFile : mapMultiArgs["-loadblock"]) + for (auto const& strFile : gArgs.GetArgs("-loadblock")) { FILE *file = fsbridge::fopen(strFile, "rb"); if (file) { @@ -1067,7 +1316,10 @@ bool AppInit2(ThreadHandlerPtr threads) if (file) { fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LoadExternalBlockFile(file); - RenameOver(pathBootstrap, pathBootstrapOld); + if (!RenameOver(pathBootstrap, pathBootstrapOld)) + { + uiInterface.InitMessage(_("Failed to rename bootstrap file to .old for backup purposes.")); + } } g_timer.GetTimes("load bootstrap file complete", "init"); @@ -1078,7 +1330,7 @@ bool AppInit2(ThreadHandlerPtr threads) // Ban manager instance should not already be instantiated assert(!g_banman); // Create ban manager instance. - g_banman = std::make_unique(GetDataDir() / "banlist.dat", &uiInterface, GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); + g_banman = std::make_unique(GetDataDir() / "banlist.dat", &uiInterface, gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); uiInterface.InitMessage(_("Loading addresses...")); LogPrint(BCLog::LogFlags::NOISY, "Loading addresses..."); @@ -1115,8 +1367,7 @@ bool AppInit2(ThreadHandlerPtr threads) if (!threads->createThread(StartNode, NULL, "Start Thread")) InitError(_("Error: could not start node")); - if (fServer) - StartRPCThreads(); + if (fServer) StartRPCThreads(); // ********************************************************* Step 12: finished diff --git a/src/init.h b/src/init.h index 54c6cb6ece..9395c1b8c8 100644 --- a/src/init.h +++ b/src/init.h @@ -20,7 +20,13 @@ void Shutdown(void* parg); bool AppInit2(ThreadHandlerPtr threads); void ThreadAppInit2(ThreadHandlerPtr th); -std::string HelpMessage(); +void AddLoggingArgs(ArgsManager& argsman); + +/** + * Register all arguments with the ArgsManager + */ +void SetupServerArgs(); + std::string VersionMessage(); std::string LogSomething(); diff --git a/src/logging.cpp b/src/logging.cpp index 0291641d1c..0e45abff70 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -6,6 +6,7 @@ #include #include #include "util/time.h" +#include "util/system.h" #include #include @@ -15,10 +16,7 @@ #include #include -// externs unavoidable because these are in util.h. -extern fs::path &GetDataDir(bool fNetSpecific = true); -extern bool GetBoolArg(const std::string& strArg, bool fDefault); -extern int64_t GetArg(const std::string& strArg, int64_t nDefault); +extern ArgsManager gArgs; const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; @@ -368,7 +366,7 @@ void BCLog::Logger::ShrinkDebugFile() bool BCLog::Logger::archive(bool fImmediate, fs::path pfile_out) { - bool fArchiveDaily = GetBoolArg("-logarchivedaily", true); + bool fArchiveDaily = gArgs.GetBoolArg("-logarchivedaily", true); int64_t nTime = GetAdjustedTime(); boost::gregorian::date ArchiveCheckDate = boost::posix_time::from_time_t(nTime).date(); @@ -470,11 +468,11 @@ bool BCLog::Logger::archive(bool fImmediate, fs::path pfile_out) fs::remove(pfile_temp); - bool fDeleteOldLogArchives = GetBoolArg("-deleteoldlogarchives", true); + bool fDeleteOldLogArchives = gArgs.GetBoolArg("-deleteoldlogarchives", true); if (fDeleteOldLogArchives) { - unsigned int nRetention = (unsigned int)GetArg("-logarchiveretainnumfiles", 30); + unsigned int nRetention = (unsigned int)gArgs.GetArg("-logarchiveretainnumfiles", 30); LogPrintf ("INFO: Logger: nRetention %i.", nRetention); std::set> SortedDirEntries; diff --git a/src/main.cpp b/src/main.cpp index 31e7e516e7..1f9662b443 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -606,7 +606,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB - if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx)) + if (dFreeCount > gArgs.GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx)) return error("AcceptToMemoryPool : free transaction rejected by rate limiter"); LogPrint(BCLog::LogFlags::MEMPOOL, "Rate limit dFreeCount: %g => %g", dFreeCount, dFreeCount+nSize); @@ -2037,7 +2037,7 @@ bool SetBestChain(CTxDB& txdb, CBlock &blockNew, CBlockIndex* pindexNew) else LogPrintf("{SBC} new best {%s %d} ; ",hashBestChain.ToString(), nBestHeight); - std::string strCmd = GetArg("-blocknotify", ""); + std::string strCmd = gArgs.GetArg("-blocknotify", ""); if (!fIsInitialDownload && !strCmd.empty()) { boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); @@ -2758,7 +2758,7 @@ bool LoadBlockIndex(bool fAllowNew) nCoinbaseMaturity = 10; // test maturity is 10 blocks nGrandfather = 196550; //1-24-2016 - MAX_OUTBOUND_CONNECTIONS = (int)GetArg("-maxoutboundconnections", 8); + MAX_OUTBOUND_CONNECTIONS = (int)gArgs.GetArg("-maxoutboundconnections", 8); } LogPrintf("Mode=%s", fTestNet ? "TestNet" : "Prod"); @@ -3160,7 +3160,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nTrust = 0; // Allow newbies to connect easily with 0 blocks - if (GetArgument("autoban","true") == "true") + if (gArgs.GetArg("-autoban", "true") == "true") { // Note: Hacking attempts start in this area @@ -3203,7 +3203,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Moved the below from AddTimeData to here to follow bitcoin's approach. int64_t nOffsetSample = nTime - GetTime(); pfrom->nTimeOffset = nOffsetSample; - if (GetBoolArg("-synctime", true)) + if (gArgs.GetBoolArg("-synctime", true)) AddTimeData(pfrom->addr, nOffsetSample); // Change version diff --git a/src/main.h b/src/main.h index fcd2554734..7119f78a18 100644 --- a/src/main.h +++ b/src/main.h @@ -469,7 +469,7 @@ class CBlock : public CBlockHeader { // Take last bit of block hash as entropy bit unsigned int nEntropyBit = ((GetHash(true).GetUint64()) & 1llu); - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printstakemodifier")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printstakemodifier")) LogPrintf("GetStakeEntropyBit: hashBlock=%s nEntropyBit=%u", GetHash(true).ToString(), nEntropyBit); return nEntropyBit; } diff --git a/src/miner.cpp b/src/miner.cpp index 16debdb146..55e5b02061 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -267,18 +267,18 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) CoinBase.vout[0].SetEmpty(); // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); + unsigned int nBlockMaxSize = gArgs.GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::clamp(nBlockMaxSize, 1000, MAX_BLOCK_SIZE - 1000); // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); + unsigned int nBlockPrioritySize = gArgs.GetArg("-blockprioritysize", 27000); nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); // Minimum block size you want to create; block will be filled with free transactions // until there are no more or the block reaches this size: - unsigned int nBlockMinSize = GetArg("-blockminsize", 0); + unsigned int nBlockMinSize = gArgs.GetArg("-blockminsize", 0); nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); // Fee-per-kilobyte amount considered the same as "free" @@ -287,8 +287,8 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) // 1-satoshi-fee transactions. It should be set above the real // cost to you of processing a transaction. int64_t nMinTxFee = GetBaseFee(CoinBase); - if (mapArgs.count("-mintxfee")) - ParseMoney(mapArgs["-mintxfee"], nMinTxFee); + if (gArgs.IsArgSet("-mintxfee")) + ParseMoney(gArgs.GetArg("-mintxfee", "dummy"), nMinTxFee); // Collect memory pool transactions into the block int64_t nFees = 0; @@ -519,7 +519,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) nBlockSigOps += nTxSigOps; nFees += nTxFees; - if (LogInstance().WillLogCategory(BCLog::LogFlags::NOISY) || GetBoolArg("-printpriority")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::NOISY) || gArgs.GetBoolArg("-printpriority")) { LogPrintf("priority %.1f feeperkb %.1f txid %s", dPriority, dFeePerKb, tx.GetHash().ToString()); @@ -544,7 +544,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) } } - if (LogInstance().WillLogCategory(BCLog::LogFlags::NOISY) || GetBoolArg("-printpriority")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::NOISY) || gArgs.GetBoolArg("-printpriority")) LogPrintf("CreateNewBlock(): total size %" PRIu64, nBlockSize); } @@ -1120,7 +1120,7 @@ bool CreateGridcoinReward( } claim.m_client_version = FormatFullVersion().substr(0, GRC::Claim::MAX_VERSION_SIZE); - claim.m_organization = GetArgument("org", "").substr(0, GRC::Claim::MAX_ORGANIZATION_SIZE); + claim.m_organization = gArgs.GetArg("-org", "").substr(0, GRC::Claim::MAX_ORGANIZATION_SIZE); // Do a dry run for the claim signature to ensure that we can sign for a // researcher claim. We generate the final signature when signing all of @@ -1189,16 +1189,16 @@ bool GetSideStakingStatusAndAlloc(SideStakeAlloc& vSideStakeAlloc) double dAllocation = 0.0; double dSumAllocation = 0.0; - bool fEnableSideStaking = GetBoolArg("-enablesidestaking"); + bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking"); LogPrint(BCLog::LogFlags::MINER, "StakeMiner: fEnableSideStaking = %u", fEnableSideStaking); // If side staking is enabled, parse destinations and allocations. We don't need to worry about any that are rejected // other than a warning message, because any unallocated rewards will go back into the coinstake output(s). if (fEnableSideStaking) { - if (mapArgs.count("-sidestake") && mapMultiArgs["-sidestake"].size() > 0) + if (gArgs.GetArgs("-sidestake").size()) { - for (auto const& sSubParam : mapMultiArgs["-sidestake"]) + for (auto const& sSubParam : gArgs.GetArgs("-sidestake")) { ParseString(sSubParam, ',', vSubParam); if (vSubParam.size() != 2) @@ -1270,14 +1270,14 @@ bool GetSideStakingStatusAndAlloc(SideStakeAlloc& vSideStakeAlloc) bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficiency, int64_t& nDesiredStakeOutputValue) { // Parse StakeSplit and SideStaking flags. - bool fEnableStakeSplit = GetBoolArg("-enablestakesplit"); + bool fEnableStakeSplit = gArgs.GetBoolArg("-enablestakesplit"); LogPrint(BCLog::LogFlags::MINER, "StakeMiner: fEnableStakeSplit = %u", fEnableStakeSplit); // If stake output splitting is enabled, determine efficiency and minimum stake split value. if (fEnableStakeSplit) { // Pull efficiency for UTXO staking from config, but constrain to the interval [0.75, 0.98]. Use default of 0.90. - dEfficiency = (double)GetArg("-stakingefficiency", 90) / 100; + dEfficiency = (double)gArgs.GetArg("-stakingefficiency", 90) / 100; if (dEfficiency > 0.98) dEfficiency = 0.98; else if (dEfficiency < 0.75) @@ -1287,7 +1287,7 @@ bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficie // Pull Minimum Post Stake UTXO Split Value from config or command line parameter. // Default to 800 and do not allow it to be specified below 800 GRC. - nMinStakeSplitValue = max(GetArg("-minstakesplitvalue", MIN_STAKE_SPLIT_VALUE_GRC), MIN_STAKE_SPLIT_VALUE_GRC) + nMinStakeSplitValue = max(gArgs.GetArg("-minstakesplitvalue", MIN_STAKE_SPLIT_VALUE_GRC), MIN_STAKE_SPLIT_VALUE_GRC) * COIN; LogPrint(BCLog::LogFlags::MINER, "StakeMiner: nMinStakeSplitValue = %f", CoinToDouble(nMinStakeSplitValue)); diff --git a/src/net.cpp b/src/net.cpp index d3df270f51..f11997a3d8 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -122,7 +122,7 @@ void AddOneShot(string strDest) unsigned short GetListenPort() { - return (unsigned short)(GetArg("-port", GetDefaultPort())); + return (unsigned short)(gArgs.GetArg("-port", GetDefaultPort())); } void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) @@ -513,7 +513,7 @@ bool CNode::Misbehaving(int howmuch) mapMisbehavior[addr] = std::make_pair(nMisbehavior, GetAdjustedTime()); - if (nMisbehavior >= GetArg("-banscore", 100)) + if (nMisbehavior >= gArgs.GetArg("-banscore", 100)) { LogPrint(BCLog::LogFlags::NET, "Misbehaving: %s (%d -> %d) DISCONNECTING", addr.ToString(), nMisbehavior-howmuch, nMisbehavior); @@ -541,9 +541,9 @@ int CNode::GetMisbehavior() const // The default banscore is normally 100, but can be changed by specifying -banscore on the command line. At the default setting, // This results in a decay of roughly 100/24 = 4 points per hour. int time_based_decay_correction = std::round( - (double) GetArg("-banscore", 100) + (double) gArgs.GetArg("-banscore", 100) * (double) std::max((int64_t) 0, GetAdjustedTime() - iMisbehavior->second.second) - / (double) GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME) + / (double) gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME) ); // Make sure nMisbehavior doesn't go below zero. @@ -940,11 +940,11 @@ void ThreadSocketHandler2(void* parg) if (nErr != WSAEWOULDBLOCK) LogPrintf("socket error accept INVALID_SOCKET: %d", nErr); } - else if (nInbound >= GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS) + else if (nInbound >= gArgs.GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS) { LogPrint(BCLog::LogFlags::NET, "Surpassed max inbound connections maxconnections:%" PRId64 " minus max_outbound:%i", - GetArg("-maxconnections", 125), + gArgs.GetArg("-maxconnections", 125), MAX_OUTBOUND_CONNECTIONS); closesocket(hSocket); @@ -1488,12 +1488,12 @@ void ThreadOpenConnections2(void* parg) LogPrint(BCLog::LogFlags::NET, "ThreadOpenConnections started"); // Connect to specific addresses - if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) + if (gArgs.GetArgs("-connect").size()) { for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - for (auto const& strAddr : mapMultiArgs["-connect"]) + for (auto const& strAddr : gArgs.GetArgs("-connect")) { CAddress addr; OpenNetworkConnection(addr, NULL, strAddr.c_str()); @@ -1629,12 +1629,12 @@ void ThreadOpenAddedConnections2(void* parg) { LogPrint(BCLog::LogFlags::NET, "ThreadOpenAddedConnections started"); - if (mapArgs.count("-addnode") == 0) + if (gArgs.GetArgs("-addnode").empty()) return; if (HaveNameProxy()) { while(!fShutdown) { - for (auto const& strAddNode : mapMultiArgs["-addnode"]) { + for (auto const& strAddNode : gArgs.GetArgs("-addnode")) { CAddress addr; CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(addr, &grant, strAddNode.c_str()); @@ -1646,7 +1646,7 @@ void ThreadOpenAddedConnections2(void* parg) } vector > vservAddressesToAdd(0); - for (auto const& strAddNode : mapMultiArgs["-addnode"]) + for (auto const& strAddNode : gArgs.GetArgs("-addnode")) { vector vservNode(0); if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) @@ -1992,15 +1992,15 @@ void StartNode(void* parg) // Make this thread recognisable as the startup thread RenameThread("grc-start"); fShutdown = false; - MAX_OUTBOUND_CONNECTIONS = (int)GetArg("-maxoutboundconnections", 8); + MAX_OUTBOUND_CONNECTIONS = (int)gArgs.GetArg("-maxoutboundconnections", 8); int nMaxOutbound = 0; if (semOutbound == NULL) { // initialize semaphore - nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)GetArg("-maxconnections", 125)); + nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)gArgs.GetArg("-maxconnections", 125)); semOutbound = new CSemaphore(nMaxOutbound); } - LogPrintf("Using %i OutboundConnections with a MaxConnections of %" PRId64, MAX_OUTBOUND_CONNECTIONS, GetArg("-maxconnections", 125)); + LogPrintf("Using %i OutboundConnections with a MaxConnections of %" PRId64, MAX_OUTBOUND_CONNECTIONS, gArgs.GetArg("-maxconnections", 125)); if (pnodeLocalHost == NULL) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); @@ -2011,7 +2011,7 @@ void StartNode(void* parg) // Start threads // - if (!GetBoolArg("-dnsseed", true)) + if (!gArgs.GetBoolArg("-dnsseed", true)) LogPrintf("DNS seeding disabled"); else if (!netThreads->createThread(ThreadDNSAddressSeed,NULL,"ThreadDNSAddressSeed")) @@ -2042,7 +2042,7 @@ void StartNode(void* parg) LogPrintf("Error: createThread(ThreadDumpAddress) failed"); // Mine proof-of-stake blocks in the background - if (!GetBoolArg("-staking", true)) + if (!gArgs.GetBoolArg("-staking", true)) LogPrintf("Staking disabled"); else if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner")) diff --git a/src/net.h b/src/net.h index c92b516b32..11f8da096b 100644 --- a/src/net.h +++ b/src/net.h @@ -36,8 +36,8 @@ extern int PEER_TIMEOUT; typedef int64_t NodeId; -inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } -inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } +inline unsigned int ReceiveFloodSize() { return 1000*gArgs.GetArg("-maxreceivebuffer", 5*1000); } +inline unsigned int SendBufferSize() { return 1000*gArgs.GetArg("-maxsendbuffer", 1*1000); } void AddOneShot(std::string strDest); bool RecvLine(SOCKET hSocket, std::string& strLine); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 6970aa4aa0..bb4a853328 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -73,6 +73,27 @@ static QSplashScreen *splashref; int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& optionsModel); +static void SetupUIArgs(ArgsManager& argsman) +{ + argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), + ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-lang=", "Set language, for example \"de_DE\" (default: system locale)", + ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + + //TODO: Implement -resetguisettings. For right now this just does the same as -choosedatadir. + argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", + ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-splash", "Show splash screen on startup (default: 1)", + ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-style", "Specify GUI style for Qt to use on Windows and MacOS (default: fusion)", + ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-suppressnetworkgraph", "Suppress network graph (default: 0)", + ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-showorphans", "Include stale (orphaned) coinstake transactions in the transaction list", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); +} + static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style) { // Message from network thread @@ -230,15 +251,33 @@ int main(int argc, char *argv[]) g_timer.InitTimer("default", false); SetupEnvironment(); + SetupServerArgs(); + SetupUIArgs(gArgs); // Note every function above the InitLogging() call must use fprintf or similar. // Command-line options take precedence: // Before this would of been done in main then config file loaded. // We will load config file here as well. - ParseParameters(argc, argv); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); + return EXIT_FAILURE; + } + /** Check mainnet config file first in case testnet is set there and not in command line args **/ SelectParams(CBaseChainParams::MAIN); + // Currently unused. + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) { + ThreadSafeMessageBox(strprintf("Error reading configuration file.\n"), + "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); + QMessageBox::critical(nullptr, PACKAGE_NAME, + QObject::tr("Error: Cannot parse configuration file.")); + return EXIT_FAILURE; + } + // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #if QT_VERSION >= 0x050600 @@ -259,7 +298,7 @@ int main(int argc, char *argv[]) // as it is used to locate QSettings) app.setOrganizationName("Gridcoin"); //XXX app.setOrganizationDomain(""); - if(GetBoolArg("-testnet")) // Separate UI settings for testnet + if(gArgs.GetBoolArg("-testnet")) // Separate UI settings for testnet app.setApplicationName("Gridcoin-Qt-testnet"); else app.setApplicationName("Gridcoin-Qt"); @@ -273,7 +312,7 @@ int main(int argc, char *argv[]) // the override on Linux for now so that a user's window manager Qt theme // comes through for widgets without an explicit application style. // - if (!IsArgSet("-style")) { + if (!gArgs.IsArgSet("-style")) { app.setStyle("Fusion"); } #endif @@ -294,7 +333,7 @@ int main(int argc, char *argv[]) OptionsModel optionsModel; // Get desired locale (e.g. "de_DE") from command line or use system locale - QString lang_territory = QString::fromStdString(GetArg("-lang", QLocale::system().name().toStdString())); + QString lang_territory = QString::fromStdString(gArgs.GetArg("-lang", QLocale::system().name().toStdString())); QString lang = lang_territory; // Convert to "de" only by truncating "_DE" lang.truncate(lang_territory.lastIndexOf('_')); @@ -325,14 +364,17 @@ int main(int argc, char *argv[]) // Gracefully exit if the user cancels if (!Intro::showIfNeeded(did_show_intro)) return EXIT_SUCCESS; + // Do this to pickup -testnet from the command line. + SelectParams(gArgs.IsArgSet("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); + // Determine availability of data directory and parse gridcoinresearch.conf // Do not call GetDataDir(true) before this step finishes if (!CheckDataDirOption()) { - ThreadSafeMessageBox(strprintf("Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "")), + ThreadSafeMessageBox(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")), "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Specified data directory \"%1\" does not exist.") - .arg(QString::fromStdString(GetArg("-datadir", "")))); + .arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } @@ -353,15 +395,22 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (!ReadConfigFile(mapArgs, mapMultiArgs)) { - ThreadSafeMessageBox(strprintf("Error reading configuration file.\n"), + // Reread config file after correct chain is selected + if (!gArgs.ReadConfigFiles(error, true)) { + ThreadSafeMessageBox(strprintf("Error reading configuration file: %s\n", error), "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); QMessageBox::critical(nullptr, PACKAGE_NAME, - QObject::tr("Error: Cannot parse configuration file.")); + QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error))); return EXIT_FAILURE; } - SelectParams(mapArgs.count("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); + if (!gArgs.InitSettings(error)) { + ThreadSafeMessageBox(strprintf("Error initializing settings.\n"), + "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); + QMessageBox::critical(nullptr, PACKAGE_NAME, + QObject::tr("Error initializing settings: %1").arg(QString::fromStdString(error))); + return EXIT_FAILURE; + } // Initialize logging as early as possible. InitLogging(); @@ -370,7 +419,7 @@ int main(int argc, char *argv[]) ipcScanRelay(argc, argv); // Make sure a user does not request snapshotdownload and resetblockchaindata at same time! - if (mapArgs.count("-snapshotdownload") && mapArgs.count("-resetblockchaindata")) + if (gArgs.IsArgSet("-snapshotdownload") && gArgs.IsArgSet("-resetblockchaindata")) { LogPrintf("-snapshotdownload and -resetblockchaindata cannot be used in conjunction"); @@ -378,7 +427,7 @@ int main(int argc, char *argv[]) } // Run snapshot main if Gridcoin was started with the snapshot argument and we are not TestNet - if (mapArgs.count("-snapshotdownload") && !mapArgs.count("-testnet")) + if (gArgs.IsArgSet("-snapshotdownload") && !gArgs.IsArgSet("-testnet")) { GRC::Upgrade snapshot; @@ -401,7 +450,7 @@ int main(int argc, char *argv[]) } // Check to see if the user requested to reset blockchain data -- We allow on testnet. - if (mapArgs.count("-resetblockchaindata")) + if (gArgs.IsArgSet("-resetblockchaindata")) { GRC::Upgrade resetblockchain; @@ -519,7 +568,7 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt // Show help message immediately after parsing command-line options (for "-lang") and setting locale, // but before showing splash screen. - if (mapArgs.count("-?") || mapArgs.count("-help")) + if (HelpRequested(gArgs)) { GUIUtil::HelpMessageBox help; help.showOrPrint(); @@ -527,7 +576,7 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt } QSplashScreen splash(QPixmap(":/images/splash")); - if (GetBoolArg("-splash", true) && !GetBoolArg("-min")) + if (gArgs.GetBoolArg("-splash", true) && !gArgs.GetBoolArg("-min")) { splash.setEnabled(false); splash.show(); @@ -579,7 +628,7 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt window.setWalletModel(&walletModel); // If -min option passed, start window minimized. - if(GetBoolArg("-min")) + if(gArgs.GetBoolArg("-min")) { window.showMinimized(); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4acab47a02..528e9d6c8e 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -523,7 +523,7 @@ void BitcoinGUI::createMenuBar() file->addAction(verifyMessageAction); file->addSeparator(); - if (!GetBoolArg("-testnet", false)) + if (!gArgs.GetBoolArg("-testnet", false)) { file->addAction(snapshotAction); } @@ -651,7 +651,7 @@ void BitcoinGUI::createToolBars() QFrame *frameBlocks = new QFrame(); // Show a red label in the status bar for testnet: - if (GetBoolArg("-testnet")) { + if (gArgs.GetBoolArg("-testnet")) { QLabel *testnetLabel = new QLabel(); testnetLabel->setObjectName("testnetStatusLabel"); testnetLabel->setText("TESTNET"); @@ -688,7 +688,7 @@ void BitcoinGUI::createToolBars() //12-21-2015 Prevent Lock from falling off the page frameBlocksLayout->addStretch(); - if (GetBoolArg("-staking", true)) + if (gArgs.GetBoolArg("-staking", true)) { QTimer *timerStakingIcon = new QTimer(labelStakingIcon); connect(timerStakingIcon, SIGNAL(timeout()), this, SLOT(updateStakingIcon())); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b4069f6486..0b51642b7e 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -99,7 +99,7 @@ void ClientModel::updateTimer() emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers); } - if (GetArg("-suppressnetworkgraph", "false") != "true") + if (gArgs.GetArg("-suppressnetworkgraph", "false") != "true") { emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); } diff --git a/src/qt/diagnosticsdialog.cpp b/src/qt/diagnosticsdialog.cpp index 15dbed3f57..3315da11d0 100644 --- a/src/qt/diagnosticsdialog.cpp +++ b/src/qt/diagnosticsdialog.cpp @@ -761,7 +761,7 @@ void DiagnosticsDialog::VerifyBoincPath() fs::path boincPath = (fs::path) GRC::GetBoincDataDir(); if (boincPath.empty()) - boincPath = (fs::path) GetArgument("boincdatadir", ""); + boincPath = (fs::path) gArgs.GetArg("-boincdatadir", ""); boincPath = boincPath / "client_state.xml"; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 3027c27c21..6e8d5e779b 100755 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -432,7 +432,7 @@ AutoStartupArguments GetAutoStartupArguments(bool fStartMin = true) for (const auto& flag : { "-scraper", "-explorer" }) { - if (GetBoolArg(flag)) + if (gArgs.GetBoolArg(flag)) { (result.arguments += " ") += flag; } @@ -644,20 +644,15 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) : header = "gridcoinresearch " + tr("version") + " " + QString::fromStdString(FormatFullVersion()) + "\n\n" + tr("Usage:") + "\n" + - " gridcoin-qt [" + tr("command-line options") + "] " + "\n"; + " gridcoinresearch [" + tr("command-line options") + "] " + "\n"; - coreOptions = QString::fromStdString(HelpMessage()); + options = QString::fromStdString(gArgs.GetHelpMessage()); - uiOptions = tr("UI options") + ":\n" + - " -lang= " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + - " -min " + tr("Start minimized") + "\n" + - " -splash " + tr("Show splash screen on startup (default: 1)") + "\n"; - - setWindowTitle(tr("Gridcoin-Qt")); + setWindowTitle(tr("Gridcoin")); setTextFormat(Qt::PlainText); // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider. setText(header + QString(QChar(0x2003)).repeated(50)); - setDetailedText(coreOptions + "\n" + uiOptions); + setDetailedText(options); setStandardButtons(QMessageBox::Ok); setDefaultButton(QMessageBox::Ok); @@ -667,7 +662,7 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) : void HelpMessageBox::printToConsole() { // On other operating systems, the expected action is to print the message to the console. - QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions; + QString strUsage = header + "\n" + options; fprintf(stdout, "%s", strUsage.toStdString().c_str()); } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index c6ba6f684d..c6f2cc8265 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -150,8 +150,7 @@ namespace GUIUtil private: QString header; - QString coreOptions; - QString uiOptions; + QString options; }; } // namespace GUIUtil diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 8158b2dccc..1c21b882da 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -171,7 +171,7 @@ bool Intro::showIfNeeded(bool& did_show_intro) QSettings settings; /* If data directory provided on command line, no need to look at settings or show a picking dialog */ - if(!GetArg("-datadir", "").empty()) + if(!gArgs.GetArg("-datadir", "").empty()) return true; /* 1) Default data directory for operating system */ QString dataDir = GUIUtil::getDefaultDataDirectory(); @@ -179,14 +179,14 @@ bool Intro::showIfNeeded(bool& did_show_intro) dataDir = settings.value("dataDir", dataDir).toString(); if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) - || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) + || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() - || GetBoolArg("-resetguisettings", false)) + || gArgs.GetBoolArg("-resetguisettings", false)) { /* Use selectParams here to guarantee Params() can be used by node interface when we implement it from Bitcoin. */ try { - SelectParams(mapArgs.count("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); + SelectParams(gArgs.IsArgSet("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); } catch (const std::exception&) { return false; } @@ -223,7 +223,7 @@ bool Intro::showIfNeeded(bool& did_show_intro) * (to be consistent with bitcoind behavior) */ if (dataDir != GUIUtil::getDefaultDataDirectory()) { - SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting + gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting } return true; diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 622c8ce297..7219246236 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -58,22 +58,22 @@ void OptionsModel::Init() // These are shared with core Bitcoin; we want // command-line options to override the GUI settings: if (settings.contains("fUseUPnP")) { - SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()); + gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()); } if (settings.contains("addrProxy") && settings.value("fUseProxy").toBool()) { - SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); + gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); } if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) { - SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); + gArgs.SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); } if (!language.isEmpty()) { - SoftSetArg("-lang", language.toStdString()); + gArgs.SoftSetArg("-lang", language.toStdString()); } if (settings.contains("fDisableUpdateCheck")) { - SoftSetBoolArg("-disableupdatecheck", settings.value("fDisableUpdateCheck").toBool()); + gArgs.SoftSetBoolArg("-disableupdatecheck", settings.value("fDisableUpdateCheck").toBool()); } if (settings.contains("dataDir") && dataDir != GUIUtil::getDefaultDataDirectory()) { - SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(settings.value("dataDir").toString()).string()); + gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(settings.value("dataDir").toString()).string()); } } @@ -98,7 +98,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const case DisableTrxNotifications: return QVariant(fDisableTrxNotifications); case MapPortUPnP: - return settings.value("fUseUPnP", GetBoolArg("-upnp", true)); + return settings.value("fUseUPnP", gArgs.GetBoolArg("-upnp", true)); case MinimizeOnClose: return QVariant(fMinimizeOnClose); case ProxyUse: @@ -136,9 +136,9 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const case LimitTxnDate: return QVariant(limitTxnDate); case DisableUpdateCheck: - return QVariant(GetBoolArg("-disableupdatecheck", false)); + return QVariant(gArgs.GetBoolArg("-disableupdatecheck", false)); case DataDir: - return settings.value("dataDir", QString::fromStdString(GetArg("-datadir", GetDataDir().string()))); + return settings.value("dataDir", QString::fromStdString(gArgs.GetArg("-datadir", GetDataDir().string()))); default: return QVariant(); } @@ -260,7 +260,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("limitTxnDate", limitTxnDate); break; case DisableUpdateCheck: - SetArgument("disableupdatecheck", value.toBool() ? "1" : "0"); + gArgs.ForceSetArg("-disableupdatecheck", value.toBool() ? "1" : "0"); settings.setValue("fDisableUpdateCheck", value.toBool()); break; case DataDir: diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 388ef02053..f8f4b081d7 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -40,7 +40,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & if(!showInactive && (status == TransactionStatus::Conflicted || status == TransactionStatus::NotAccepted)) return false; //1-2-2015 Halford - Mask Orphans from User View so they do not complain - if (!GetBoolArg("-showorphans", false)) + if (!gArgs.GetBoolArg("-showorphans", false)) if (status == TransactionStatus::Conflicted || status == TransactionStatus::NotAccepted) return false; if(!(TYPE(type) & typeFilter)) diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index e2994058f0..ee98f8f5ea 100755 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -14,7 +14,7 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx, bool datetime_limi if (wtx.IsCoinStake() && !wtx.IsInMainChain()) { // Show stale (orphaned) staking transactions if requested: - return GetBoolArg("-showorphans", false); + return gArgs.GetBoolArg("-showorphans", false); } if (wtx.IsCoinBase()) diff --git a/src/qt/upgradeqt.cpp b/src/qt/upgradeqt.cpp index b2c7be1c87..c7b70d97ee 100644 --- a/src/qt/upgradeqt.cpp +++ b/src/qt/upgradeqt.cpp @@ -331,7 +331,7 @@ void UpgradeQt::DeleteSnapshot() { // File is out of scope now check if it exists and if so delete it. // This covers partial downloaded files or a http response downloaded into file. - std::string snapshotfile = GetArg("-snapshoturl", "https://download.gridcoin.us/download/downloadstake/signed/snapshot.zip"); + std::string snapshotfile = gArgs.GetArg("-snapshoturl", "https://download.gridcoin.us/download/downloadstake/signed/snapshot.zip"); size_t pos = snapshotfile.find_last_of("/"); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 46d61871cf..40f49bfbdc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1238,7 +1238,13 @@ UniValue resetcpids(const UniValue& params, bool fHelp) LOCK(cs_main); - ReadConfigFile(mapArgs, mapMultiArgs); + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) + { + throw JSONRPCError(RPC_MISC_ERROR, error_msg); + } + GRC::Researcher::Reload(); res.pushKV("Reset", 1); @@ -1659,7 +1665,12 @@ UniValue readconfig(const UniValue& params, bool fHelp) LOCK(cs_main); - ReadConfigFile(mapArgs, mapMultiArgs); + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) + { + throw JSONRPCError(RPC_MISC_ERROR, error_msg); + } res.pushKV("readconfig", 1); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index dc0f6dd21a..82958741f1 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -35,25 +35,25 @@ using namespace boost::asio; UniValue CallRPC(const string& strMethod, const UniValue& params) { - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + if (!gArgs.IsArgSet("-rpcuser") || !gArgs.IsArgSet("-rpcpassword")) throw runtime_error(strprintf( _("You must set rpcpassword= in the configuration file:\n%s\n" "If the file does not exist, create it with owner-readable-only file permissions."), GetConfigFile().string())); // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); + bool fUseSSL = gArgs.GetBoolArg("-rpcssl"); ioContext io_context; ssl::context context(ssl::context::sslv23); context.set_options(ssl::context::no_sslv2); asio::ssl::stream sslStream(io_context, context); SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream< SSLIOStreamDevice > stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", ToString(GetDefaultRPCPort())))) + if (!d.connect(gArgs.GetArg("-rpcconnect", "127.0.0.1"), gArgs.GetArg("-rpcport", ToString(GetDefaultRPCPort())))) throw runtime_error("couldn't connect to server"); // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + string strUserPass64 = EncodeBase64(gArgs.GetArg("-rpcuser", "dummy") + ":" + gArgs.GetArg("-rpcpassword", "dummy")); map mapRequestHeaders; mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index fbfb060e0b..f91e956062 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -478,7 +478,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) { entry.pushKV("label", item->second); - if (GetBoolArg("-enableaccounts", false)) + if (gArgs.GetBoolArg("-enableaccounts", false)) entry.pushKV("account", item->second); } } @@ -1326,7 +1326,7 @@ UniValue scanforunspent(const UniValue& params, bool fHelp) std::string exportfile = params[0].get_str() + "-" + std::string(boTime) + "." + params[4].get_str(); - std::string backupdir = GetArg("-backupdir", ""); + std::string backupdir = gArgs.GetArg("-backupdir", ""); if (backupdir.empty()) exportpath = GetDataDir() / "walletbackups" / "rpc" / exportfile; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e479896629..0a0b0f36b9 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -42,9 +42,9 @@ static boost::thread_group* rpc_worker_group = NULL; const UniValue emptyobj(UniValue::VOBJ); -unsigned short GetDefaultRPCPort() +int GetDefaultRPCPort() { - return GetBoolArg("-testnet", false) ? 25715 : 15715; + return BaseParams().RPCPort(); } void RPCTypeCheck(const UniValue& params, @@ -497,7 +497,7 @@ bool ClientAllowed(const boost::asio::ip::address& address) return true; const string strAddress = address.to_string(); - const vector& vAllow = mapMultiArgs["-rpcallowip"]; + const vector& vAllow = gArgs.GetArgs("-rpcallowip"); for (auto const& strAllow : vAllow) if (WildcardMatch(strAddress, strAllow)) return true; @@ -574,16 +574,17 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor void StartRPCThreads() { - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if ((mapArgs["-rpcpassword"] == "") || - (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) + strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); + + if ((gArgs.GetArg("-rpcpassword", "") == "" || + (gArgs.GetArg("-rpcuser", "") == gArgs.GetArg("-rpcpassword", "")))) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); string strWhatAmI = "To use gridcoind"; - if (mapArgs.count("-server")) + if (gArgs.IsArgSet("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); - else if (mapArgs.count("-daemon")) + else if (gArgs.IsArgSet("-daemon")) strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); uiInterface.ThreadSafeMessageBox(strprintf( _("%s, you must set a rpcpassword in the configuration file:\n %s\n" @@ -603,7 +604,7 @@ void StartRPCThreads() return; } - const bool fUseSSL = GetBoolArg("-rpcssl"); + const bool fUseSSL = gArgs.GetBoolArg("-rpcssl"); assert(rpc_io_service == NULL); rpc_io_service = new ioContext(); @@ -613,24 +614,24 @@ void StartRPCThreads() { rpc_ssl_context->set_options(ssl::context::no_sslv2); - fs::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); + fs::path pathCertFile(gArgs.GetArg("-rpcsslcertificatechainfile", "server.cert")); if (!pathCertFile.is_absolute()) pathCertFile = fs::path(GetDataDir()) / pathCertFile; if (fs::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); - fs::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); + fs::path pathPKFile(gArgs.GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_absolute()) pathPKFile = fs::path(GetDataDir()) / pathPKFile; if (fs::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); - string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); + string strCiphers = gArgs.GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); SSL_CTX_set_cipher_list(rpc_ssl_context->native_handle(), strCiphers.c_str()); } // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets - const bool loopback = !mapArgs.count("-rpcallowip"); + const bool loopback = !gArgs.IsArgSet("-rpcallowip"); asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); + ip::tcp::endpoint endpoint(bindAddress, gArgs.GetArg("-rpcport", GetDefaultRPCPort())); boost::system::error_code v6_only_error; boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); @@ -688,7 +689,7 @@ void StartRPCThreads() } rpc_worker_group = new boost::thread_group(); - for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + for (int i = 0; i < gArgs.GetArg("-rpcthreads", 4); i++) rpc_worker_group->create_thread(boost::bind(&ioContext::run, rpc_io_service)); } @@ -821,7 +822,7 @@ void ServiceConnection(AcceptedConnection *conn) /* Deter brute-forcing short passwords. If this results in a DOS the user really shouldn't have their RPC port exposed.*/ - if (mapArgs["-rpcpassword"].size() < 20) + if (gArgs.GetArgs("-rpcpassword").size() < 20) MilliSleep(250); conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; diff --git a/src/rpc/server.h b/src/rpc/server.h index c985a992d5..761b22f713 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -256,4 +256,4 @@ extern UniValue vote(const UniValue& params, bool fHelp); extern UniValue votebyid(const UniValue& params, bool fHelp); extern UniValue votedetails(const UniValue& params, bool fHelp); -unsigned short GetDefaultRPCPort(); +int GetDefaultRPCPort(); diff --git a/src/script.cpp b/src/script.cpp index 2521507514..5d63cfa3a3 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1241,7 +1241,7 @@ class CSignatureCache // (~200 bytes per cache entry times 50,000 entries) // Since there are a maximum of 20,000 signature operations per block // 50,000 is a reasonable default. - int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + int64_t nMaxCacheSize = gArgs.GetArg("-maxsigcachesize", 50000); if (nMaxCacheSize <= 0) return; LOCK(cs_sigcache); diff --git a/src/sync.h b/src/sync.h index 3795136e8e..a92ed4f631 100644 --- a/src/sync.h +++ b/src/sync.h @@ -45,14 +45,53 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII // // /////////////////////////////// + +#ifdef DEBUG_LOCKORDER +template +void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false); +void LeaveCritical(); +void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line); +std::string LocksHeld(); +template +void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs); +template +void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs); +void DeleteLock(void* cs); +bool LockStackEmpty(); + +/** + * Call abort() if a potential lock order deadlock bug is detected, instead of + * just logging information and throwing a logic_error. Defaults to true, and + * set to false in DEBUG_LOCKORDER unit tests. + */ +extern bool g_debug_lockorder_abort; +#else +template +inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {} +inline void LeaveCritical() {} +inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {} +template +inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {} +template +void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {} +inline void DeleteLock(void* cs) {} +inline bool LockStackEmpty() { return true; } +#endif +#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) +#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs) + /** - * Template mixin that adds -Wthread-safety locking - * annotations to a subset of the mutex API. + * Template mixin that adds -Wthread-safety locking annotations and lock order + * checking to a subset of the mutex API. */ template class LOCKABLE AnnotatedMixin : public PARENT { public: + ~AnnotatedMixin() { + DeleteLock((void*)this); + } + void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); @@ -67,36 +106,29 @@ class LOCKABLE AnnotatedMixin : public PARENT { return PARENT::try_lock(); } -}; -#ifdef DEBUG_LOCKORDER -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); -void LeaveCritical(); -std::string LocksHeld(); -void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); -void DeleteLock(void* cs); -#else -void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} -void static inline LeaveCritical() {} -void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} -void static inline DeleteLock(void* cs) {} -#endif -#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) + using UniqueLock = std::unique_lock; +#ifdef __clang__ + //! For negative capabilities in the Clang Thread Safety Analysis. + //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction + //! with the ! operator, to indicate that a mutex should not be held. + const AnnotatedMixin& operator!() const { return *this; } +#endif // __clang__ +}; +/** + * Wrapped mutex (legacy): supports recursive locking, but no waiting + * TODO: We should move away from using the recursive lock by default. + */ +using CCriticalSection = AnnotatedMixin; /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ -class CCriticalSection : public AnnotatedMixin -{ -public: - ~CCriticalSection() { - DeleteLock((void*)this); - } -}; +using RecursiveMutex = AnnotatedMixin; /** Wrapped mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin CWaitableCriticalSection; +typedef AnnotatedMixin Mutex; /** Just a typedef for std::condition_variable, can be wrapped later if desired */ typedef std::condition_variable CConditionVariable; @@ -165,23 +197,128 @@ class SCOPED_LOCKABLE CCriticalBlock } }; +/** Wrapper around std::unique_lock style lock for Mutex. */ +template +class SCOPED_LOCKABLE UniqueLock : public Base +{ +private: + void Enter(const char* pszName, const char* pszFile, int nLine) + { + EnterCritical(pszName, pszFile, nLine, Base::mutex()); +#ifdef DEBUG_LOCKCONTENTION + if (!Base::try_lock()) { + PrintLockContention(pszName, pszFile, nLine); +#endif + Base::lock(); +#ifdef DEBUG_LOCKCONTENTION + } +#endif + } + + bool TryEnter(const char* pszName, const char* pszFile, int nLine) + { + EnterCritical(pszName, pszFile, nLine, Base::mutex(), true); + Base::try_lock(); + if (!Base::owns_lock()) + LeaveCritical(); + return Base::owns_lock(); + } + +public: + UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) + { + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + + UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) + { + if (!pmutexIn) return; + + *static_cast(this) = Base(*pmutexIn, std::defer_lock); + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + + ~UniqueLock() UNLOCK_FUNCTION() + { + if (Base::owns_lock()) + LeaveCritical(); + } + + operator bool() + { + return Base::owns_lock(); + } + +protected: + // needed for reverse_lock + UniqueLock() { } + +public: + /** + * An RAII-style reverse lock. Unlocks on construction and locks on destruction. + */ + class reverse_lock { + public: + explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) { + CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line); + lock.unlock(); + LeaveCritical(); + lock.swap(templock); + } + + ~reverse_lock() { + templock.swap(lock); + EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex()); + lock.lock(); + } + + private: + reverse_lock(reverse_lock const&); + reverse_lock& operator=(reverse_lock const&); + + UniqueLock& lock; + UniqueLock templock; + std::string lockname; + const std::string file; + const int line; + }; + friend class reverse_lock; +}; + +#define REVERSE_LOCK(g) typename std::decay::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) + + #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) -#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) -#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) -#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) +template +using DebugLock = UniqueLock::type>::type>; + +#define LOCK(cs) DebugLock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) +#define LOCK2(cs1, cs2) \ + DebugLock criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ + DebugLock criticalblock2(cs2, #cs2, __FILE__, __LINE__); +#define TRY_LOCK(cs, name) DebugLock name(cs, #cs, __FILE__, __LINE__, true) +#define WAIT_LOCK(cs, name) DebugLock name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ { \ - EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ + EnterCritical(#cs, __FILE__, __LINE__, &cs); \ (cs).lock(); \ } -#define LEAVE_CRITICAL_SECTION(cs) \ - { \ - (cs).unlock(); \ - LeaveCritical(); \ +#define LEAVE_CRITICAL_SECTION(cs) \ + { \ + std::string lockname; \ + CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \ + (cs).unlock(); \ + LeaveCritical(); \ } class CSemaphore @@ -261,7 +398,7 @@ class CSemaphoreGrant CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} - CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) + explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { if (fTry) TryAcquire(); diff --git a/src/test/dos_tests.cpp b/src/test/dos_tests.cpp index 76eb47d8c0..592b843620 100755 --- a/src/test/dos_tests.cpp +++ b/src/test/dos_tests.cpp @@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) { CNodeStats nodestats; g_banman->ClearBanned(); - mapArgs["-banscore"] = "111"; // because 11 is my favorite number + gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number CAddress addr1(ip(0xa0b0c001)); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); dummyNode1.Misbehaving(100); @@ -64,7 +64,9 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) BOOST_CHECK(nodestats.nMisbehavior == 110); // nMisbehavior should be 110. dummyNode1.Misbehaving(1); BOOST_CHECK(g_banman->IsBanned(addr1)); - mapArgs.erase("-banscore"); + + // TODO: Why no ClearArg? + gArgs.ForceSetArg("-banscore", "100"); } BOOST_AUTO_TEST_CASE(DoS_bantime) @@ -271,14 +273,14 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig); // Exercise -maxsigcachesize code: - mapArgs["-maxsigcachesize"] = "10"; + gArgs.ForceSetArg("-maxsigcachesize","10"); // Generate a new, different signature for vin[0] to trigger cache clear: CScript oldSig = tx.vin[0].scriptSig; BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0)); BOOST_CHECK(tx.vin[0].scriptSig != oldSig); for (unsigned int j = 0; j < tx.vin.size(); j++) BOOST_CHECK(VerifySignature(orphans[j], tx, j, SIGHASH_ALL)); - mapArgs.erase("-maxsigcachesize"); + gArgs.ForceSetArg("-maxsigcachesize", "50000"); LimitOrphanTxSize(0); } diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 78953d296f..ebad94bdcc 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -2,166 +2,193 @@ #include #include -#include "util.h" +#include "util/system.h" BOOST_AUTO_TEST_SUITE(getarg_tests) -static void -ResetArgs(const std::string& strArg) +static void AddArgs(const std::string& strArg) { std::vector vecArg; boost::split(vecArg, strArg, boost::is_space(), boost::token_compress_on); + for (const auto& arg : vecArg) + { + gArgs.AddArg(arg, arg, ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + } +} + +static bool ResetArgs(const std::string& strAddArg, const std::string& strArgIn = std::string()) +{ + gArgs.ClearArgs(); + AddArgs(strAddArg); + + std::string strArg = strArgIn.empty() ? strAddArg : strArgIn; + + std::vector vecArg; + boost::split(vecArg, strArg, boost::is_space(), boost::token_compress_on); + + // Insert dummy executable name: - vecArg.insert(vecArg.begin(), "testbitcoin"); + vecArg.insert(vecArg.begin(), "testgridcoin"); // Convert to char*: std::vector vecChar; BOOST_FOREACH(std::string& s, vecArg) vecChar.push_back(s.c_str()); - ParseParameters(vecChar.size(), &vecChar[0]); + std::string error; + + bool status = gArgs.ParseParameters(vecChar.size(), &vecChar[0], error); + + if (!status) printf("ERROR: %s: %s", __func__, error.c_str()); + + return status; } BOOST_AUTO_TEST_CASE(boolarg) { - ResetArgs("-foo"); - BOOST_CHECK(GetBoolArg("-foo")); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-fo")); - BOOST_CHECK(!GetBoolArg("-fo", false)); - BOOST_CHECK(GetBoolArg("-fo", true)); + BOOST_CHECK(ResetArgs("-foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-fooo")); - BOOST_CHECK(!GetBoolArg("-fooo", false)); - BOOST_CHECK(GetBoolArg("-fooo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-fo")); + BOOST_CHECK(!gArgs.GetBoolArg("-fo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-fo", true)); - ResetArgs("-foo=0"); - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-fooo")); + BOOST_CHECK(!gArgs.GetBoolArg("-fooo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-fooo", true)); - ResetArgs("-foo=1"); - BOOST_CHECK(GetBoolArg("-foo")); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); + BOOST_CHECK(ResetArgs("-foo", "-foo=0")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + + BOOST_CHECK(ResetArgs("-foo", "-foo=1")); + BOOST_CHECK(gArgs.GetBoolArg("-foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); // New 0.6 feature: auto-map -nosomething to !-something: - ResetArgs("-nofoo"); - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); - - ResetArgs("-nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); - - ResetArgs("-foo -nofoo"); // -foo should win - BOOST_CHECK(GetBoolArg("-foo")); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); - - ResetArgs("-foo=1 -nofoo=1"); // -foo should win - BOOST_CHECK(GetBoolArg("-foo")); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); - - ResetArgs("-foo=0 -nofoo=0"); // -foo should win - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); + BOOST_CHECK(ResetArgs("-foo", "-nofoo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + + BOOST_CHECK(ResetArgs("-foo", "-nofoo=1")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + + // TODO: Did Bitcoin change the order of precedence of negation? + // Before -foo should win. Now it is the last argument? + BOOST_CHECK(ResetArgs("-foo", "-foo -nofoo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + + BOOST_CHECK(ResetArgs("-foo", "-foo=1 -nofoo=1")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + + // A double negative? + BOOST_CHECK(ResetArgs("-foo", "-foo=0 -nofoo=0")); + BOOST_CHECK(gArgs.GetBoolArg("-foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); // New 0.6 feature: treat -- same as -: - ResetArgs("--foo=1"); - BOOST_CHECK(GetBoolArg("-foo")); - BOOST_CHECK(GetBoolArg("-foo", false)); - BOOST_CHECK(GetBoolArg("-foo", true)); - - ResetArgs("--nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", false)); - BOOST_CHECK(!GetBoolArg("-foo", true)); - + BOOST_CHECK(ResetArgs("-foo", "--foo=1")); + BOOST_CHECK(gArgs.GetBoolArg("-foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); + + BOOST_CHECK(ResetArgs("-foo", "--nofoo=1")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); } BOOST_AUTO_TEST_CASE(stringarg) { - ResetArgs(""); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "eleven"); + BOOST_CHECK(ResetArgs("")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven"); - ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), ""); + BOOST_CHECK(ResetArgs("-foo -bar")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), ""); - ResetArgs("-foo="); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), ""); + BOOST_CHECK(ResetArgs("-foo", "-foo=")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), ""); - ResetArgs("-foo=11"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), "11"); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "11"); + BOOST_CHECK(ResetArgs("-foo", "-foo=11")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "11"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "11"); - ResetArgs("-foo=eleven"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), "eleven"); - BOOST_CHECK_EQUAL(GetArg("-foo", "eleven"), "eleven"); + BOOST_CHECK(ResetArgs("-foo", "-foo=eleven")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "eleven"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven"); } BOOST_AUTO_TEST_CASE(intarg) { - ResetArgs(""); - BOOST_CHECK_EQUAL(GetArg("-foo", 11), 11); - BOOST_CHECK_EQUAL(GetArg("-foo", 0), 0); + BOOST_CHECK(ResetArgs("")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0); - ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(GetArg("-foo", 11), 0); - BOOST_CHECK_EQUAL(GetArg("-bar", 11), 0); + BOOST_CHECK(ResetArgs("-foo -bar")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0); - ResetArgs("-foo=11 -bar=12"); - BOOST_CHECK_EQUAL(GetArg("-foo", 0), 11); - BOOST_CHECK_EQUAL(GetArg("-bar", 11), 12); + BOOST_CHECK(ResetArgs("-foo -bar", "-foo=11 -bar=12")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 11); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 12); - ResetArgs("-foo=NaN -bar=NotANumber"); - BOOST_CHECK_EQUAL(GetArg("-foo", 1), 0); - BOOST_CHECK_EQUAL(GetArg("-bar", 11), 0); + BOOST_CHECK(ResetArgs("-foo -bar", "-foo=NaN -bar=NotANumber")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 1), 0); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0); } BOOST_AUTO_TEST_CASE(doubledash) { - ResetArgs("--foo"); - BOOST_CHECK_EQUAL(GetBoolArg("-foo"), true); + BOOST_CHECK(ResetArgs("-foo", "--foo")); + BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo"), true); - ResetArgs("--foo=verbose --bar=1"); - BOOST_CHECK_EQUAL(GetArg("-foo", ""), "verbose"); - BOOST_CHECK_EQUAL(GetArg("-bar", 0), 1); + BOOST_CHECK(ResetArgs("-foo -bar", "--foo=verbose --bar=1")); + BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "verbose"); + BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 0), 1); } BOOST_AUTO_TEST_CASE(boolargno) { - ResetArgs("-nofoo"); - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-foo", false)); - - ResetArgs("-nofoo=1"); - BOOST_CHECK(!GetBoolArg("-foo")); - BOOST_CHECK(!GetBoolArg("-foo", true)); - BOOST_CHECK(!GetBoolArg("-foo", false)); - - ResetArgs("-nofoo=0"); - BOOST_CHECK(GetBoolArg("-foo")); - BOOST_CHECK(GetBoolArg("-foo", true)); - BOOST_CHECK(GetBoolArg("-foo", false)); - - ResetArgs("-foo --nofoo"); - BOOST_CHECK(GetBoolArg("-foo")); - - ResetArgs("-nofoo -foo"); // foo always wins: - BOOST_CHECK(GetBoolArg("-foo")); + BOOST_CHECK(ResetArgs("-foo", "-nofoo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + + BOOST_CHECK(ResetArgs("-foo", "-nofoo=1")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); + + BOOST_CHECK(ResetArgs("-foo", "-nofoo=0")); + BOOST_CHECK(gArgs.GetBoolArg("-foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); + BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); + + // TODO: Did Bitcoin change the order of precedence of negation? + // Before -foo should win. Now it is the last argument? + BOOST_CHECK(ResetArgs("-foo", "-foo --nofoo")); + BOOST_CHECK(!gArgs.GetBoolArg("-foo")); + + BOOST_CHECK(ResetArgs("-foo", "-nofoo -foo")); + BOOST_CHECK(gArgs.GetBoolArg("-foo")); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/gridcoin/researcher_tests.cpp b/src/test/gridcoin/researcher_tests.cpp index bcd03571ef..e611accaa8 100644 --- a/src/test/gridcoin/researcher_tests.cpp +++ b/src/test/gridcoin/researcher_tests.cpp @@ -16,6 +16,7 @@ #include namespace { + //! //! \brief Attempt to locate the test data directory that contains a BOINC //! client_state.xml stub file. @@ -169,7 +170,7 @@ BOOST_AUTO_TEST_CASE(it_initializes_with_project_data) BOOST_AUTO_TEST_CASE(it_parses_a_project_xml_string) { - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // The XML string contains a subset of data found within a element // from BOINC's client_state.xml file: @@ -196,12 +197,12 @@ BOOST_AUTO_TEST_CASE(it_parses_a_project_xml_string) BOOST_CHECK(project.m_error == GRC::MiningProject::Error::NONE); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); } BOOST_AUTO_TEST_CASE(it_falls_back_to_compute_a_missing_external_cpid) { - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // A bug in BOINC sometimes results in the empty empty external CPID field // as shown below. This will recompute the CPID from the internal CPID and @@ -231,7 +232,7 @@ BOOST_AUTO_TEST_CASE(it_falls_back_to_compute_a_missing_external_cpid) BOOST_CHECK(project.m_error == GRC::MiningProject::Error::NONE); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); } BOOST_AUTO_TEST_CASE(it_normalizes_project_names) @@ -411,7 +412,7 @@ BOOST_AUTO_TEST_CASE(it_is_iterable) BOOST_AUTO_TEST_CASE(it_parses_a_set_of_project_xml_sections) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); GRC::MiningProjectMap projects = GRC::MiningProjectMap::Parse({ R"XML( @@ -466,7 +467,7 @@ BOOST_AUTO_TEST_CASE(it_parses_a_set_of_project_xml_sections) } // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -686,12 +687,12 @@ BOOST_AUTO_TEST_CASE(it_initializes_with_researcher_context_data) BOOST_AUTO_TEST_CASE(it_converts_a_configured_email_address_to_lowercase) { - SetArgument("email", "RESEARCHER@EXAMPLE.COM"); + gArgs.ForceSetArg("email", "RESEARCHER@EXAMPLE.COM"); BOOST_CHECK(GRC::Researcher::Email() == "researcher@example.com"); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); } BOOST_AUTO_TEST_CASE(it_provides_access_to_a_global_researcher_singleton) @@ -770,7 +771,7 @@ BOOST_AUTO_TEST_CASE(it_provides_an_overall_status_of_the_researcher_context) BOOST_AUTO_TEST_CASE(it_parses_project_xml_to_a_global_researcher_singleton) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); GRC::Researcher::Reload(GRC::MiningProjectMap::Parse({ R"XML( @@ -829,14 +830,14 @@ BOOST_AUTO_TEST_CASE(it_parses_project_xml_to_a_global_researcher_singleton) } // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); GRC::Researcher::Reload(GRC::MiningProjectMap()); } BOOST_AUTO_TEST_CASE(it_looks_up_loaded_boinc_projects_by_name) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); GRC::Researcher::Reload(GRC::MiningProjectMap::Parse({ R"XML( @@ -868,7 +869,7 @@ BOOST_AUTO_TEST_CASE(it_looks_up_loaded_boinc_projects_by_name) } // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -883,7 +884,7 @@ BOOST_AUTO_TEST_CASE(it_resets_to_investor_mode_when_parsing_no_projects) BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); GRC::Researcher::Reload(GRC::MiningProjectMap::Parse({ // Required team mismatch: @@ -1091,7 +1092,7 @@ BOOST_AUTO_TEST_CASE(it_tags_invalid_projects_with_errors_when_parsing_xml) BOOST_CHECK(!GRC::Researcher::Get()->HasRAC()); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1130,7 +1131,7 @@ BOOST_AUTO_TEST_CASE(it_skips_loading_project_xml_with_empty_project_names) BOOST_AUTO_TEST_CASE(it_skips_the_team_requirement_when_set_by_protocol) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // Simulate a protocol control directive that disables the team requirement: WriteCache(Section::PROTOCOL, "REQUIRE_TEAM_WHITELIST_MEMBERSHIP", "false", 1); @@ -1171,7 +1172,7 @@ BOOST_AUTO_TEST_CASE(it_skips_the_team_requirement_when_set_by_protocol) BOOST_CHECK(GRC::Researcher::Get()->HasRAC()); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); DeleteCache(Section::PROTOCOL, "REQUIRE_TEAM_WHITELIST_MEMBERSHIP"); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1179,7 +1180,7 @@ BOOST_AUTO_TEST_CASE(it_skips_the_team_requirement_when_set_by_protocol) BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_when_set_by_the_protocol) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // Simulate a protocol control directive with whitelisted teams: WriteCache(Section::PROTOCOL, "TEAM_WHITELIST", "team 1|Team 2", 1); @@ -1262,7 +1263,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_when_set_by_the_protocol) BOOST_CHECK(!GRC::Researcher::Get()->HasRAC()); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); DeleteCache(Section::PROTOCOL, "TEAM_WHITELIST"); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1270,7 +1271,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_when_set_by_the_protocol) BOOST_AUTO_TEST_CASE(it_applies_the_team_requirement_dynamically) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); GRC::Researcher::Reload(GRC::MiningProjectMap::Parse({ R"XML( @@ -1329,7 +1330,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_requirement_dynamically) } // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); DeleteCache(Section::PROTOCOL, "REQUIRE_TEAM_WHITELIST_MEMBERSHIP"); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1337,7 +1338,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_requirement_dynamically) BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_dynamically) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); GRC::Researcher::Reload(GRC::MiningProjectMap::Parse({ R"XML( @@ -1456,7 +1457,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_dynamically) } // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); DeleteCache(Section::PROTOCOL, "TEAM_WHITELIST"); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1464,7 +1465,7 @@ BOOST_AUTO_TEST_CASE(it_applies_the_team_whitelist_dynamically) BOOST_AUTO_TEST_CASE(it_ignores_the_team_whitelist_without_the_team_requirement) { // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // Simulate a protocol control directive that disables the team requirement: WriteCache(Section::PROTOCOL, "REQUIRE_TEAM_WHITELIST_MEMBERSHIP", "false", 1); @@ -1529,7 +1530,7 @@ BOOST_AUTO_TEST_CASE(it_ignores_the_team_whitelist_without_the_team_requirement) } // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); DeleteCache(Section::PROTOCOL, "REQUIRE_TEAM_WHITELIST_MEMBERSHIP"); DeleteCache(Section::PROTOCOL, "TEAM_WHITELIST"); RemoveTestBeacon(GRC::Cpid::Parse("f5d8234352e5a5ae3915debba7258294")); @@ -1544,11 +1545,11 @@ void it_parses_project_xml_from_a_client_state_xml_file() // the projects and CPIDs into the global researcher context. // External CPIDs generated with this email address: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // Set the directory from which the wallet will open the client_state.xml. // We point it at our test stub: - SetArgument("boincdatadir", ResolveStubDir().string() + "/"); + gArgs.ForceSetArg("boincdatadir", ResolveStubDir().string() + "/"); // Read the stub and load projects and CPIDs into the researcher context: GRC::Researcher::Reload(); @@ -1606,8 +1607,8 @@ void it_parses_project_xml_from_a_client_state_xml_file() BOOST_CHECK(GRC::Researcher::Get()->HasRAC()); // Clean up: - SetArgument("email", ""); - SetArgument("boincdatadir", ""); + gArgs.ForceSetArg("email", ""); + gArgs.ForceSetArg("boincdatadir", ""); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1615,15 +1616,15 @@ void it_parses_project_xml_from_a_client_state_xml_file() // resolve the client_state.xml stub. void it_resets_to_investor_mode_when_explicitly_configured() { - SetArgument("investor", "1"); + gArgs.ForceSetArg("investor", "1"); // For a valid test, set the email address because it will also pass falsely // if this is absent: - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); // For a valid test, ensure that we can access the client_state.xml stub // because it will also pass falsely if this is unset: - SetArgument("boincdatadir", ResolveStubDir().string() + "/"); + gArgs.ForceSetArg("boincdatadir", ResolveStubDir().string() + "/"); GRC::Researcher::Reload(); @@ -1631,9 +1632,9 @@ void it_resets_to_investor_mode_when_explicitly_configured() BOOST_CHECK(GRC::Researcher::Get()->Projects().empty() == true); // Clean up: - SetArgument("investor", "0"); - SetArgument("email", ""); - SetArgument("boincdatadir", ""); + gArgs.ForceSetArg("investor", "0"); + gArgs.ForceSetArg("email", ""); + gArgs.ForceSetArg("boincdatadir", ""); GRC::Researcher::Reload(GRC::MiningProjectMap()); } @@ -1654,7 +1655,7 @@ BOOST_AUTO_TEST_CASE(client_state_stub_exists) BOOST_AUTO_TEST_CASE(it_resets_to_investor_when_it_only_finds_pool_projects) { const GRC::Cpid cpid = GRC::Cpid::Parse("f5d8234352e5a5ae3915debba7258294"); - SetArgument("email", "researcher@example.com"); + gArgs.ForceSetArg("email", "researcher@example.com"); AddTestBeacon(cpid); // External CPID is a pool CPID: @@ -1700,14 +1701,14 @@ BOOST_AUTO_TEST_CASE(it_resets_to_investor_when_it_only_finds_pool_projects) BOOST_CHECK(GRC::Researcher::Get()->Status() != GRC::ResearcherStatus::POOL); // Clean up: - SetArgument("email", ""); + gArgs.ForceSetArg("email", ""); RemoveTestBeacon(cpid); GRC::Researcher::Reload(GRC::MiningProjectMap()); } BOOST_AUTO_TEST_CASE(it_allows_pool_operators_to_load_pool_cpids) { - SetArgument("pooloperator", "1"); + gArgs.ForceSetArg("pooloperator", "1"); // External CPID is a pool CPID: GRC::Researcher::Reload(GRC::MiningProjectMap::Parse({ @@ -1728,7 +1729,7 @@ BOOST_AUTO_TEST_CASE(it_allows_pool_operators_to_load_pool_cpids) BOOST_CHECK(GRC::Researcher::Get()->Status() != GRC::ResearcherStatus::POOL); // Clean up: - SetArgument("pooloperator", "0"); + gArgs.ForceSetArg("pooloperator", "0"); GRC::Researcher::Reload(GRC::MiningProjectMap()); } diff --git a/src/test/gridcoin_tests.cpp b/src/test/gridcoin_tests.cpp index 3c5eb46316..37c192a423 100755 --- a/src/test/gridcoin_tests.cpp +++ b/src/test/gridcoin_tests.cpp @@ -82,6 +82,7 @@ BOOST_AUTO_TEST_CASE(gridcoin_DefaultCBRShouldBe10) { CBlockIndex index; index.nTime = 1538066417; + BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), DEFAULT_CBR); } @@ -114,7 +115,7 @@ BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldClampTo2xDefault) CBlockIndex index; index.nTime = time; - WriteCache(Section::PROTOCOL, "blockreward1", ToString(DEFAULT_CBR * 2.1), time); + WriteCache(Section::PROTOCOL, "blockreward1", ToString(DEFAULT_CBR * 3), time); BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), DEFAULT_CBR * 2); } diff --git a/src/test/test_gridcoin.cpp b/src/test/test_gridcoin.cpp index d610fb0c81..9765a05d0b 100644 --- a/src/test/test_gridcoin.cpp +++ b/src/test/test_gridcoin.cpp @@ -19,10 +19,14 @@ extern bool fPrintToConsole; extern void noui_connect(); extern leveldb::Options GetOptions(); + struct TestingSetup { TestingSetup() { + fs::path m_path_root = fs::temp_directory_path() / "test_common_" PACKAGE_NAME / GetRandHash().ToString(); fPrintToDebugger = true; // don't want to write to debug.log file fUseFastIndex = true; // Don't verify block hashes when loading + gArgs.ForceSetArg("-datadir", m_path_root.string()); + gArgs.ClearPathCache(); SelectParams(CBaseChainParams::MAIN); // TODO: Refactor CTxDB to something like bitcoin's current CDBWrapper and remove this workaround. leveldb::Options db_options; @@ -39,7 +43,7 @@ struct TestingSetup { // Ban manager instance should not already be instantiated assert(!g_banman); // Create ban manager instance. - g_banman = std::make_unique(GetDataDir() / "banlist.dat", &uiInterface, GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); + g_banman = std::make_unique(GetDataDir() / "banlist.dat", &uiInterface, gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME)); } ~TestingSetup() { diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 7ecbd39dd7..606228353c 100755 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -147,48 +147,69 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) { const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; - ParseParameters(0, (char**)argv_test); - BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); + gArgs.AddArg("-a", "-a", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-b", "-b", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-ccc", "--ccc", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - ParseParameters(1, (char**)argv_test); - BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); + std::string error; - ParseParameters(5, (char**)argv_test); + // TODO: Finish fixing this for the new Bitcoin port. + + //BOOST_CHECK(gArgs.ParseParameters(0, (char**)argv_test, error)); + //BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); + + //BOOST_CHECK(gArgs.ParseParameters(1, (char**)argv_test, error)); + //BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty()); + + BOOST_CHECK(gArgs.ParseParameters(5, (char**)argv_test, error)); // expectation: -ignored is ignored (program name argument), // -a, -b and -ccc end up in map, -d ignored because it is after // a non-option argument (non-GNU option parsing) - BOOST_CHECK(mapArgs.size() == 3 && mapMultiArgs.size() == 3); - BOOST_CHECK(mapArgs.count("-a") && mapArgs.count("-b") && mapArgs.count("-ccc") - && !mapArgs.count("f") && !mapArgs.count("-d")); - BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc") - && !mapMultiArgs.count("f") && !mapMultiArgs.count("-d")); + // BOOST_CHECK(mapArgs.size() == 3 && mapMultiArgs.size() == 3); + BOOST_CHECK(gArgs.GetArgs("-a").size() == 1 && gArgs.GetArgs("-b").size() == 1 && gArgs.GetArgs("-ccc").size() == 2 + && gArgs.GetArgs("f").empty() && gArgs.GetArgs("-d").empty()); + //BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc") + // && !mapMultiArgs.count("f") && !mapMultiArgs.count("-d")); - BOOST_CHECK(mapArgs["-a"] == "" && mapArgs["-ccc"] == "multiple"); - BOOST_CHECK(mapMultiArgs["-ccc"].size() == 2); + //BOOST_CHECK(mapArgs["-a"] == "" && mapArgs["-ccc"] == "multiple"); + //BOOST_CHECK(mapMultiArgs["-ccc"].size() == 2); } BOOST_AUTO_TEST_CASE(util_GetArg) { - mapArgs.clear(); - mapArgs["strtest1"] = "string..."; + gArgs.ClearArgs(); + + gArgs.ForceSetArg("-strtest1", "string..."); + //mapArgs["strtest1"] = "string..."; + // strtest2 undefined on purpose - mapArgs["inttest1"] = "12345"; - mapArgs["inttest2"] = "81985529216486895"; + + gArgs.ForceSetArg("-inttest1", "12345"); + //mapArgs["inttest1"] = "12345"; + gArgs.ForceSetArg("-inttest2", "81985529216486895"); + //mapArgs["inttest2"] = "81985529216486895"; + // inttest3 undefined on purpose - mapArgs["booltest1"] = ""; + + gArgs.ForceSetArg("-booltest1", ""); + //mapArgs["booltest1"] = ""; + // booltest2 undefined on purpose - mapArgs["booltest3"] = "0"; - mapArgs["booltest4"] = "1"; - BOOST_CHECK_EQUAL(GetArg("strtest1", "default"), "string..."); - BOOST_CHECK_EQUAL(GetArg("strtest2", "default"), "default"); - BOOST_CHECK_EQUAL(GetArg("inttest1", -1), 12345); - BOOST_CHECK_EQUAL(GetArg("inttest2", -1), 81985529216486895LL); - BOOST_CHECK_EQUAL(GetArg("inttest3", -1), -1); - BOOST_CHECK_EQUAL(GetBoolArg("booltest1"), true); - BOOST_CHECK_EQUAL(GetBoolArg("booltest2"), false); - BOOST_CHECK_EQUAL(GetBoolArg("booltest3"), false); - BOOST_CHECK_EQUAL(GetBoolArg("booltest4"), true); + gArgs.ForceSetArg("-booltest3", "0"); + //mapArgs["booltest3"] = "0"; + gArgs.ForceSetArg("-booltest4", "1"); + //mapArgs["booltest4"] = "1"; + + BOOST_CHECK_EQUAL(gArgs.GetArg("strtest1", "default"), "string..."); + BOOST_CHECK_EQUAL(gArgs.GetArg("strtest2", "default"), "default"); + BOOST_CHECK_EQUAL(gArgs.GetArg("inttest1", -1), 12345); + BOOST_CHECK_EQUAL(gArgs.GetArg("inttest2", -1), 81985529216486895LL); + BOOST_CHECK_EQUAL(gArgs.GetArg("inttest3", -1), -1); + BOOST_CHECK_EQUAL(gArgs.GetBoolArg("booltest1"), true); + BOOST_CHECK_EQUAL(gArgs.GetBoolArg("booltest2"), false); + BOOST_CHECK_EQUAL(gArgs.GetBoolArg("booltest3"), false); + BOOST_CHECK_EQUAL(gArgs.GetBoolArg("booltest4"), true); } BOOST_AUTO_TEST_CASE(util_WildcardMatch) @@ -658,6 +679,7 @@ BOOST_AUTO_TEST_CASE(util_VerifySplit3) BOOST_CHECK_EQUAL("", res[0]); } +/* TODO: Replace this outdated with new Bitcoin equivalent. BOOST_AUTO_TEST_CASE(util_mapArgsComparator) { mapArgs.clear(); @@ -693,6 +715,7 @@ BOOST_AUTO_TEST_CASE(util_mapArgsComparator) BOOST_CHECK_EQUAL(mapArgs.size(), 4); } +*/ BOOST_AUTO_TEST_SUITE_END() diff --git a/src/threadsafety.h b/src/threadsafety.h index ec2b40f731..91197f4c2f 100644 --- a/src/threadsafety.h +++ b/src/threadsafety.h @@ -1,43 +1,42 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2020 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_THREADSAFETY_H #define BITCOIN_THREADSAFETY_H +#include + #ifdef __clang__ // TL;DR Add GUARDED_BY(mutex) to member variables. The others are // rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo); // -// See https://clang.llvm.org/docs/LanguageExtensions.html#threadsafety +// See https://clang.llvm.org/docs/ThreadSafetyAnalysis.html // for documentation. The clang compiler can do advanced static analysis // of locking when given the -Wthread-safety option. -#define LOCKABLE __attribute__ ((lockable)) -#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable)) -#define GUARDED_BY(x) __attribute__ ((guarded_by(x))) -#define GUARDED_VAR __attribute__ ((guarded_var)) -#define PT_GUARDED_BY(x) __attribute__ ((pt_guarded_by(x))) -#define PT_GUARDED_VAR __attribute__ ((pt_guarded_var)) -#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__))) -#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__))) -#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock_function(__VA_ARGS__))) -#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock_function(__VA_ARGS__))) -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__ ((exclusive_trylock_function(__VA_ARGS__))) -#define SHARED_TRYLOCK_FUNCTION(...) __attribute__ ((shared_trylock_function(__VA_ARGS__))) -#define UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__))) -#define LOCK_RETURNED(x) __attribute__ ((lock_returned(x))) -#define LOCKS_EXCLUDED(...) __attribute__ ((locks_excluded(__VA_ARGS__))) -#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__ ((exclusive_locks_required(__VA_ARGS__))) -#define SHARED_LOCKS_REQUIRED(...) __attribute__ ((shared_locks_required(__VA_ARGS__))) -#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis)) +#define LOCKABLE __attribute__((lockable)) +#define SCOPED_LOCKABLE __attribute__((scoped_lockable)) +#define GUARDED_BY(x) __attribute__((guarded_by(x))) +#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x))) +#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__))) +#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__))) +#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__))) +#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__))) +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((exclusive_trylock_function(__VA_ARGS__))) +#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((shared_trylock_function(__VA_ARGS__))) +#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__))) +#define LOCK_RETURNED(x) __attribute__((lock_returned(x))) +#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) +#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__))) +#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__))) +#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis)) +#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__))) #else #define LOCKABLE #define SCOPED_LOCKABLE #define GUARDED_BY(x) -#define GUARDED_VAR #define PT_GUARDED_BY(x) -#define PT_GUARDED_VAR #define ACQUIRED_AFTER(...) #define ACQUIRED_BEFORE(...) #define EXCLUSIVE_LOCK_FUNCTION(...) @@ -50,5 +49,29 @@ #define EXCLUSIVE_LOCKS_REQUIRED(...) #define SHARED_LOCKS_REQUIRED(...) #define NO_THREAD_SAFETY_ANALYSIS -#endif // __GNUC__ +#define ASSERT_EXCLUSIVE_LOCK(...) +#endif // __GNUC__ + +// StdMutex provides an annotated version of std::mutex for us, +// and should only be used when sync.h Mutex/LOCK/etc are not usable. +class LOCKABLE StdMutex : public std::mutex +{ +public: +#ifdef __clang__ + //! For negative capabilities in the Clang Thread Safety Analysis. + //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction + //! with the ! operator, to indicate that a mutex should not be held. + const StdMutex& operator!() const { return *this; } +#endif // __clang__ +}; + +// StdLockGuard provides an annotated version of std::lock_guard for us, +// and should only be used when sync.h Mutex/LOCK/etc are not usable. +class SCOPED_LOCKABLE StdLockGuard : public std::lock_guard +{ +public: + explicit StdLockGuard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard(cs) {} + ~StdLockGuard() UNLOCK_FUNCTION() {} +}; + #endif // BITCOIN_THREADSAFETY_H diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp index 574d50d076..2c5857b265 100644 --- a/src/txdb-leveldb.cpp +++ b/src/txdb-leveldb.cpp @@ -28,7 +28,7 @@ leveldb::DB *txdb; // global pointer for LevelDB object instance static leveldb::Options GetOptions() { leveldb::Options options; - int nCacheSizeMB = GetArg("-dbcache", 25); + int nCacheSizeMB = gArgs.GetArg("-dbcache", 25); options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); options.filter_policy = leveldb::NewBloomFilterPolicy(10); return options; @@ -447,8 +447,8 @@ bool CTxDB::LoadBlockIndex() nLoaded = 0; // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 1); - int nCheckDepth = GetArg( "-checkblocks", 1000); + int nCheckLevel = gArgs.GetArg("-checklevel", 1); + int nCheckDepth = gArgs.GetArg( "-checkblocks", 1000); LogPrintf("Verifying last %i blocks at level %i", nCheckDepth, nCheckLevel); CBlockIndex* pindexFork = NULL; diff --git a/src/util.cpp b/src/util.cpp index 9e3b66e154..9987f8743a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -36,38 +36,8 @@ namespace boost { #include #include -#ifdef WIN32 -#ifdef _MSC_VER -#pragma warning(disable:4786) -#pragma warning(disable:4804) -#pragma warning(disable:4805) -#pragma warning(disable:4717) -#endif -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0501 -#ifdef _WIN32_IE -#undef _WIN32_IE -#endif -#define _WIN32_IE 0x0501 -#define WIN32_LEAN_AND_MEAN 1 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#include /* for _commit */ -#include -#include "shlobj.h" -#elif defined(__linux__) -# include -#endif - using namespace std; -ArgsMap mapArgs; -ArgsMultiMap mapMultiArgs; - bool fPrintToConsole = false; bool fPrintToDebugger = false; bool fRequestShutdown = false; @@ -75,7 +45,6 @@ bool fShutdown = false; bool fDaemon = false; bool fServer = false; bool fCommandLine = false; -string strMiscWarning; bool fTestNet = false; bool fNoListen = false; bool fLogTimestamps = false; @@ -97,33 +66,6 @@ static std::mutex cs_dir_locks; // to be initialized here with the bitcoingui object. std::atomic_bool miner_first_pass_complete {false}; -void SetupEnvironment() -{ - // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale - // may be invalid, in which case the "C.UTF-8" locale is used as fallback. -#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) - try { - std::locale(""); // Raises a runtime error if current locale is invalid - } catch (const std::runtime_error&) { - setenv("LC_ALL", "C.UTF-8", 1); - } -#elif defined(WIN32) - // Set the default input/output charset is utf-8 - SetConsoleCP(CP_UTF8); - SetConsoleOutputCP(CP_UTF8); -#endif - // The path locale is lazy initialized and to avoid deinitialization errors - // in multithreading environments, it is set explicitly by the main thread. - // A dummy locale is used to extract the internal default locale, used by - // fs::path, which is then used to explicitly imbue the path. - std::locale loc = fs::path::imbue(std::locale::classic()); -#ifndef WIN32 - fs::path::imbue(loc); -#else - fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16())); -#endif -} - // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS @@ -246,26 +188,6 @@ int GetDayOfYear(int64_t timestamp) } } -void ParseString(const string& str, char c, vector& v) -{ - if (str.empty()) - return; - string::size_type i1 = 0; - string::size_type i2; - while (true) - { - i2 = str.find(c, i1); - if (i2 == str.npos) - { - v.push_back(str.substr(i1)); - return; - } - v.push_back(str.substr(i1, i2-i1)); - i1 = i2+1; - } -} - - string FormatMoney(int64_t n, bool fPlus) { // Note: not using straight sprintf here because we do NOT want @@ -289,7 +211,6 @@ string FormatMoney(int64_t n, bool fPlus) return str; } - bool ParseMoney(const string& str, int64_t& nRet) { return ParseMoney(str.c_str(), nRet); @@ -335,140 +256,6 @@ bool ParseMoney(const char* pszIn, int64_t& nRet) return true; } -static void InterpretNegativeSetting(string name, ArgsMap& mapSettingsRet) -{ - // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set - if (name.find("-no") == 0) - { - std::string positive("-"); - positive.append(name.begin()+3, name.end()); - if (mapSettingsRet.count(positive) == 0) - { - bool value = !GetBoolArg(name); - mapSettingsRet[positive] = (value ? "1" : "0"); - } - } -} - -void ParseParameters(int argc, const char* const argv[]) -{ - mapArgs.clear(); - mapMultiArgs.clear(); - for (int i = 1; i < argc; i++) - { - std::string str(argv[i]); - std::string strValue; - size_t is_index = str.find('='); - if (is_index != std::string::npos) - { - strValue = str.substr(is_index+1); - str = str.substr(0, is_index); - } -#ifdef WIN32 - boost::to_lower(str); - if (boost::algorithm::starts_with(str, "/")) - str = "-" + str.substr(1); -#endif - if (str[0] != '-') - break; - - mapArgs[str] = strValue; - mapMultiArgs[str].push_back(strValue); - } - - // New 0.6 features: - for (auto const& entry : mapArgs) - { - string name = entry.first; - - // interpret --foo as -foo (as long as both are not set) - if (name.find("--") == 0) - { - std::string singleDash(name.begin()+1, name.end()); - if (mapArgs.count(singleDash) == 0) - mapArgs[singleDash] = entry.second; - name = singleDash; - } - - // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set - InterpretNegativeSetting(name, mapArgs); - } -} - -std::string GetArgument(const std::string& arg, const std::string& defaultvalue) -{ - if (mapArgs.count("-" + arg)) - return mapArgs["-" + arg]; - - return defaultvalue; -} - -// SetArgument - Set or alter arguments stored in memory -void SetArgument( - const string &argKey, - const string &argValue) -{ - mapArgs["-" + argKey] = argValue; -} - -std::string GetArg(const std::string& strArg, const std::string& strDefault) -{ - if (mapArgs.count(strArg)) - return mapArgs[strArg]; - return strDefault; -} - -int64_t GetArg(const std::string& strArg, int64_t nDefault) -{ - if (mapArgs.count(strArg)) - return atoi64(mapArgs[strArg]); - return nDefault; -} - -bool GetBoolArg(const std::string& strArg, bool fDefault) -{ - if (mapArgs.count(strArg)) - { - if (mapArgs[strArg].empty()) - return true; - return (atoi(mapArgs[strArg]) != 0); - } - return fDefault; -} - -bool IsArgSet(const std::string& strArg) -{ - return !(GetArg(strArg, "never_used_as_argument") == "never_used_as_argument"); -} - -bool IsArgNegated(const std::string& strArg) -{ - return GetArg(strArg, "true") == "false"; -} - -bool SoftSetArg(const std::string& strArg, const std::string& strValue) -{ - if (mapArgs.count(strArg)) - return false; - ForceSetArg(strArg, strValue); - return true; -} - -bool SoftSetBoolArg(const std::string& strArg, bool fValue) -{ - if (fValue) - return SoftSetArg(strArg, std::string("1")); - else - return SoftSetArg(strArg, std::string("0")); -} - -void ForceSetArg(const std::string& strArg, const std::string& strValue) -{ - mapArgs[strArg] = strValue; - mapMultiArgs[strArg].clear(); - mapMultiArgs[strArg].push_back(strValue); -} - bool WildcardMatch(const char* psz, const char* mask) { while (true) @@ -498,241 +285,6 @@ bool WildcardMatch(const string& str, const string& mask) return WildcardMatch(str.c_str(), mask.c_str()); } -static std::string FormatException(std::exception* pex, const char* pszThread) -{ -#ifdef WIN32 - char pszModule[MAX_PATH] = ""; - GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); -#else - const char* pszModule = "gridcoin"; -#endif - if (pex) - return strprintf( - "EXCEPTION: %s \n%s \n%s in %s\n", typeid(*pex).name(), pex->what(), pszModule, pszThread); - else - return strprintf( - "UNKNOWN EXCEPTION \n%s in %s\n", pszModule, pszThread); -} - -void LogException(std::exception* pex, const char* pszThread) -{ - std::string message = FormatException(pex, pszThread); - LogPrintf("%s", message); -} - -void PrintException(std::exception* pex, const char* pszThread) -{ - std::string message = FormatException(pex, pszThread); - LogPrintf("\n\n************************\n%s", message); - fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); - strMiscWarning = message; - throw; -} - -void PrintExceptionContinue(std::exception* pex, const char* pszThread) -{ - std::string message = FormatException(pex, pszThread); - LogPrintf("\n\n************************\n%s", message); - fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); - strMiscWarning = message; -} - -fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) -{ - if (path.is_absolute()) { - return path; - } - return fs::absolute(path, GetDataDir(net_specific)); -} - -fs::path GetDefaultDataDir() -{ - // Windows < Vista: C:\Documents and Settings\Username\Application Data\GridcoinResearch - // Windows >= Vista: C:\Users\Username\AppData\Roaming\GridcoinResearch - // Mac: ~/Library/Application Support/GridcoinResearch - // Linux/Unix: ~/.GridcoinResearch -#ifdef WIN32 - // Windows - - // This is the user's base roaming AppData path with GridcoinResearch added. - return GetSpecialFolderPath(CSIDL_APPDATA) / "GridcoinResearch"; -#else - fs::path pathRet; - - // For everything except for Windows, use the environment variable to get - // the home path. - char* pszHome = getenv("HOME"); - - // There is no home path, so default to the root directory. - if (pszHome == nullptr || strlen(pszHome) == 0) { - pathRet = fs::path("/"); - } else { - pathRet = fs::path(pszHome); - } -#ifdef MAC_OSX - // The pathRet here represents the HOME directory. Apple - // applications are expected to store their files in - // "~/Library/Application Support/[AppDir]. - return pathRet / "Library" / "Application Support" / "GridcoinResearch"; -#else - // Linux/Unix - return pathRet / ".GridcoinResearch"; -#endif // MAC_OSX -#endif // WIN32 -} - -const fs::path &GetDataDir(bool fNetSpecific) -{ - static fs::path pathCached[2]; - static CCriticalSection cs_PathCached; - static bool cachedPath[2] = {false, false}; - - fs::path &path = pathCached[fNetSpecific]; - - // This can be called during exceptions by LogPrintf, so we cache the - // value so we don't have to do memory allocations after that. - if (cachedPath[fNetSpecific] && fs::is_directory(path)) - { - return path; - } - - LOCK(cs_PathCached); - - if (mapArgs.count("-datadir")) - { - path = fs::system_complete(mapArgs["-datadir"]); - if (!fs::is_directory(path)) - { - throw std::runtime_error("Supplied datadir is invalid! " - "Please check your datadir argument. Aborting."); - } - } - else - { - path = GetDefaultDataDir(); - } - - if (fNetSpecific && GetBoolArg("-testnet", false)) - { - path /= "testnet"; - } - - if (!fs::exists(path)) fs::create_directories(path); - - cachedPath[fNetSpecific] = true; - - return path; -} - - -bool CheckDataDirOption() -{ - std::string datadir = GetArg("-datadir", ""); - return datadir.empty() || fs::is_directory(fs::system_complete(datadir)); -} - -fs::path GetProgramDir() -{ - fs::path path; - - if (mapArgs.count("-programdir")) - { - path = fs::system_complete(mapArgs["-programdir"]); - - if (!fs::is_directory(path)) - { - path = ""; - LogPrintf("Invalid path stated in gridcoinresearch.conf"); - } - else - { - return path; - } - - } - - #ifdef WIN32 - const char* const list[] = {"gridcoind.exe", "gridcoin-qt.exe", "gridcoinupgrader.exe"}; - #elif defined MAC_OSX - const char* const list[] = {"gridcoind.exe", "gridcoin-qt.exe", "gridcoinupgrader.exe"}; - #else - const char* const list[] = {"gridcoinresearchd", "gridcoin-qt", "gridcoinupgrader"}; - #endif - - for (int i = 0; i < 3; ++i) - { - if (fs::exists((fs::current_path() / list[i]).c_str())) - { - return fs::current_path(); - } - } - - LogPrintf("Please specify program directory in config file using the 'programdir' argument"); - path = ""; - return path; - -} - -fs::path GetConfigFile() -{ - fs::path pathConfigFile(GetArg("-conf", "gridcoinresearch.conf")); - if (!pathConfigFile.is_absolute()) pathConfigFile = GetDataDir() / pathConfigFile; - return pathConfigFile; -} - -bool IsConfigFileEmpty() -{ - fsbridge::ifstream streamConfig(GetConfigFile()); - if (!streamConfig.good()) - { - return true; - } - return false; - -} - -bool ReadConfigFile(ArgsMap& mapSettingsRet, - ArgsMultiMap& mapMultiSettingsRet) -{ - fsbridge::ifstream streamConfig(GetConfigFile()); - - if (!streamConfig.good()) - return true; // No gridcoinresearch.conf file is OK - - try { - boost::iostreams::filtering_istream streamFilteredConfig; - streamFilteredConfig.push(boost::iostreams::newline_filter(boost::iostreams::newline::posix)); - streamFilteredConfig.push(streamConfig); - - set setOptions; - setOptions.insert("*"); - - for (boost::program_options::detail::config_file_iterator it(streamFilteredConfig, setOptions), end; it != end; ++it) - { - // Don't overwrite existing settings so command line settings override bitcoin.conf - string strKey = string("-") + it->string_key; - if (mapSettingsRet.count(strKey) == 0) - { - mapSettingsRet[strKey] = it->value[0]; - // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) - InterpretNegativeSetting(strKey, mapSettingsRet); - } - mapMultiSettingsRet[strKey].push_back(it->value[0]); - } - - return true; - } catch (...) { - return false; - } -} - -fs::path GetPidFile() -{ - fs::path pathPidFile(GetArg("-pid", "gridcoinresearch.pid")); - if (!pathPidFile.is_absolute()) pathPidFile = GetDataDir() / pathPidFile; - return pathPidFile; -} - #ifndef WIN32 void CreatePidFile(const fs::path &path, pid_t pid) { @@ -745,17 +297,6 @@ void CreatePidFile(const fs::path &path, pid_t pid) } #endif -bool RenameOver(fs::path src, fs::path dest) -{ -#ifdef WIN32 - return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(), - MOVEFILE_REPLACE_EXISTING); -#else - int rc = std::rename(src.string().c_str(), dest.string().c_str()); - return (rc == 0); -#endif /* WIN32 */ -} - /** * Ignores exceptions thrown by Boost's create_directories if the requested directory exists. * Specifically handles case where path p exists, but it wasn't possible for the user to @@ -775,40 +316,6 @@ bool TryCreateDirectories(const fs::path& p) return false; } -// Newer FileCommit overload from Bitcoin. -bool FileCommit(FILE *file) -{ - if (fflush(file) != 0) { // harmless if redundantly called - LogPrintf("%s: fflush failed: %d\n", __func__, errno); - return false; - } -#ifdef WIN32 - HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); - if (FlushFileBuffers(hFile) == 0) { - LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); - return false; - } -#else - #if defined(__linux__) || defined(__NetBSD__) - if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync - LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); - return false; - } - #elif defined(MAC_OSX) && defined(F_FULLFSYNC) - if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success - LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); - return false; - } - #else - if (fsync(fileno(file)) != 0 && errno != EINVAL) { - LogPrintf("%s: fsync failed: %d\n", __func__, errno); - return false; - } - #endif -#endif - return true; -} - bool DirIsWritable(const fs::path& directory) { fs::path tmpFile = directory / fs::unique_path(); @@ -1043,21 +550,6 @@ std::string FormatSubVersion(const std::string& name, int nClientVersion, const return ss.str(); } -#ifdef WIN32 -fs::path GetSpecialFolderPath(int nFolder, bool fCreate) -{ - wchar_t pszPath[MAX_PATH] = L""; - - if (SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) - { - return fs::path(pszPath); - } - - LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path."); - return fs::path(""); -} -#endif - void runCommand(std::string strCommand) { int nErr = ::system(strCommand.c_str()); @@ -1220,30 +712,3 @@ std::string TimestampToHRDate(double dtm) std::string sDt = DateTimeStrFormat("%m-%d-%Y %H:%M:%S",dtm); return sDt; } - -namespace util { -#ifdef WIN32 -WinCmdLineArgs::WinCmdLineArgs() -{ - wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc); - std::wstring_convert, wchar_t> utf8_cvt; - argv = new char*[argc]; - args.resize(argc); - for (int i = 0; i < argc; i++) { - args[i] = utf8_cvt.to_bytes(wargv[i]); - argv[i] = &*args[i].begin(); - } - LocalFree(wargv); -} - -WinCmdLineArgs::~WinCmdLineArgs() -{ - delete[] argv; -} - -std::pair WinCmdLineArgs::get() -{ - return std::make_pair(argc, argv); -} -#endif -} // namespace util diff --git a/src/util.h b/src/util.h index bd932e7eb3..bb3777f29e 100644 --- a/src/util.h +++ b/src/util.h @@ -6,10 +6,7 @@ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H -#include "attributes.h" - #include "uint256.h" -#include "fs.h" #include "fwd.h" #include "hash.h" @@ -26,15 +23,11 @@ #include #include -#include -#include "compat/assumptions.h" - // After merging some more of Bitcoin's utilities, we can split them out // of this file to reduce the header load: -#include "tinyformat.h" #include -#include "util/time.h" -#include "logging.h" +#include "util/system.h" +#include "util/string.h" #ifndef WIN32 #include @@ -81,30 +74,10 @@ #define MAX_PATH 1024 #endif -void SetupEnvironment(); - //void MilliSleep(int64_t n); extern int GetDayOfYear(int64_t timestamp); -/** - * Allows search of mapArgs and mapMultiArgs in a case insensitive way - */ - -struct mapArgscomp -{ - bool operator() (const std::string& lhs, const std::string& rhs) const - { - return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; - } -}; - -typedef std::map ArgsMap; -typedef std::map, mapArgscomp> ArgsMultiMap; - -extern ArgsMap mapArgs; -extern ArgsMultiMap mapMultiArgs; - extern bool fPrintToConsole; extern bool fPrintToDebugger; extern bool fRequestShutdown; @@ -112,7 +85,6 @@ extern bool fShutdown; extern bool fDaemon; extern bool fServer; extern bool fCommandLine; -extern std::string strMiscWarning; extern bool fTestNet; extern bool fNoListen; extern bool fLogTimestamps; @@ -127,35 +99,18 @@ void RandAddSeedPerfmon(); void LogException(std::exception* pex, const char* pszThread); void PrintException(std::exception* pex, const char* pszThread); void PrintExceptionContinue(std::exception* pex, const char* pszThread); -void ParseString(const std::string& str, char c, std::vector& v); std::string FormatMoney(int64_t n, bool fPlus=false); bool ParseMoney(const std::string& str, int64_t& nRet); bool ParseMoney(const char* pszIn, int64_t& nRet); -void ParseParameters(int argc, const char*const argv[]); bool WildcardMatch(const char* psz, const char* mask); bool WildcardMatch(const std::string& str, const std::string& mask); bool TryCreateDirectories(const fs::path& p); -bool FileCommit(FILE *fileout); std::string TimestampToHRDate(double dtm); -bool RenameOver(fs::path src, fs::path dest); -fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true); -fs::path GetDefaultDataDir(); -fs::path GetProgramDir(); - -const fs::path &GetDataDir(bool fNetSpecific = true); -bool CheckDataDirOption(); - -fs::path GetConfigFile(); -fs::path GetPidFile(); #ifndef WIN32 void CreatePidFile(const fs::path &path, pid_t pid); #endif -bool ReadConfigFile(ArgsMap& mapSettingsRet, ArgsMultiMap& mapMultiSettingsRet); -#ifdef WIN32 -fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); -#endif bool DirIsWritable(const fs::path& directory); bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false); bool TryCreateDirectories(const fs::path& p); @@ -203,20 +158,6 @@ std::string RoundToString(double d, int place); //! double RoundFromString(const std::string& s, int place); -//! -//! \brief Convert any value to a string. -//! \param val Value to convert. -//! \note This ignores locale settings. -//! -template -std::string ToString(const T& val) -{ - std::ostringstream ss; - ss.imbue(std::locale::classic()); - ss << std::fixed << val; - return ss.str(); -} - bool Contains(const std::string& data, const std::string& instring); std::vector split(const std::string& s, const std::string& delim); @@ -258,96 +199,6 @@ inline int64_t GetPerformanceCounter() return nCounter; } -inline bool IsSwitchChar(char c) -{ -#ifdef WIN32 - return c == '-' || c == '/'; -#else - return c == '-'; -#endif -} -/** - * Return string argument or default value - * - * @param strArg Argument to get (e.g. "-foo") - * @param default (e.g. "1") - * @return command-line argument or default value - */ -std::string GetArg(const std::string& strArg, const std::string& strDefault); - -/** - * Return string argument or default value - * - * @param strArg Argument to get (e.g. "foo"). Will be prefixed with "-". - * @param default (e.g. "1") - * @return command-line argument or default value - */ -std::string GetArgument(const std::string& strArg, const std::string& strDefault); - -/** - * Set argument value. - * - * @param argKey Argument to set (e.g. "foo"). Will be prefixed with "-". - * @param argValue New argument value (e.g. "1") - */ -void SetArgument(const std::string &argKey, const std::string &argValue); - -/** - * Return integer argument or default value - * - * @param strArg Argument to get (e.g. "-foo") - * @param default (e.g. 1) - * @return command-line argument (0 if invalid number) or default value - */ -int64_t GetArg(const std::string& strArg, int64_t nDefault); - -/** - * Return boolean argument or default value - * - * @param strArg Argument to get (e.g. "-foo") - * @param default (true or false) - * @return command-line argument or default value - */ -bool GetBoolArg(const std::string& strArg, bool fDefault=false); - -/** - * Return true if argument is set - * - * @param strArg Argument to get (e.g. "-foo") - * @return boolean - */ -bool IsArgSet(const std::string& strArg); - -/** - * Return true if argument is negated - * - * @param strArg Argument to get (e.g. "-foo") - * @return boolean - */ -bool IsArgNegated(const std::string& strArg); - -/** - * Set an argument if it doesn't already have a value - * - * @param strArg Argument to set (e.g. "-foo") - * @param strValue Value (e.g. "1") - * @return true if argument gets set, false if it already had a value - */ -bool SoftSetArg(const std::string& strArg, const std::string& strValue); - -/** - * Set a boolean argument if it doesn't already have a value - * - * @param strArg Argument to set (e.g. "-foo") - * @param fValue Value (e.g. false) - * @return true if argument gets set, false if it already had a value - */ -bool SoftSetBoolArg(const std::string& strArg, bool fValue); - -// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already -// been set. Also called directly in testing. -void ForceSetArg(const std::string& strArg, const std::string& strValue); - /** * MWC RNG of George Marsaglia * This is intended to be fast. It has a period of 2^59.3, though the @@ -473,21 +324,4 @@ template void TraceThread(const char* name, Callable func) } } -namespace util { -#ifdef WIN32 -class WinCmdLineArgs -{ -public: - WinCmdLineArgs(); - ~WinCmdLineArgs(); - std::pair get(); - -private: - int argc; - char** argv; - std::vector args; -}; -#endif -} // namespace util - #endif diff --git a/src/util/check.h b/src/util/check.h new file mode 100644 index 0000000000..f941597fda --- /dev/null +++ b/src/util/check.h @@ -0,0 +1,75 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_CHECK_H +#define BITCOIN_UTIL_CHECK_H + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#include + +class NonFatalCheckError : public std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + +/** + * Throw a NonFatalCheckError when the condition evaluates to false + * + * This should only be used + * - where the condition is assumed to be true, not for error handling or validating user input + * - where a failure to fulfill the condition is recoverable and does not abort the program + * + * For example in RPC code, where it is undesirable to crash the whole program, this can be generally used to replace + * asserts or recoverable logic errors. A NonFatalCheckError in RPC code is caught and passed as a string to the RPC + * caller, which can then report the issue to the developers. + */ +#define CHECK_NONFATAL(condition) \ + do { \ + if (!(condition)) { \ + throw NonFatalCheckError( \ + strprintf("%s:%d (%s)\n" \ + "Internal bug detected: '%s'\n" \ + "You may report this issue here: %s\n", \ + __FILE__, __LINE__, __func__, \ + (#condition), \ + PACKAGE_BUGREPORT)); \ + } \ + } while (false) + +#if defined(NDEBUG) +#error "Cannot compile without assertions!" +#endif + +/** Helper for Assert() */ +template +T get_pure_r_value(T&& val) +{ + return std::forward(val); +} + +/** Identity function. Abort if the value compares equal to zero */ +#define Assert(val) ([&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); assert(#val && check); return std::forward(check); }()) + +/** + * Assume is the identity function. + * + * - Should be used to run non-fatal checks. In debug builds it behaves like + * Assert()/assert() to notify developers and testers about non-fatal errors. + * In production it doesn't warn or log anything. + * - For fatal errors, use Assert(). + * - For non-fatal errors in interactive sessions (e.g. RPC or command line + * interfaces), CHECK_NONFATAL() might be more appropriate. + */ +#ifdef ABORT_ON_FAILED_ASSUME +#define Assume(val) Assert(val) +#else +#define Assume(val) ([&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); return std::forward(check); }()) +#endif + +#endif // BITCOIN_UTIL_CHECK_H diff --git a/src/util/settings.cpp b/src/util/settings.cpp new file mode 100644 index 0000000000..b92b1d30c3 --- /dev/null +++ b/src/util/settings.cpp @@ -0,0 +1,240 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +namespace util { +namespace { + +enum class Source { + FORCED, + COMMAND_LINE, + RW_SETTINGS, + CONFIG_FILE_NETWORK_SECTION, + CONFIG_FILE_DEFAULT_SECTION +}; + +//! Merge settings from multiple sources in precedence order: +//! Forced config > command line > read-write settings file > config file network-specific section > config file default section +//! +//! This function is provided with a callback function fn that contains +//! specific logic for how to merge the sources. +template +static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn) +{ + // Merge in the forced settings + if (auto* value = FindKey(settings.forced_settings, name)) { + fn(SettingsSpan(*value), Source::FORCED); + } + // Merge in the command-line options + if (auto* values = FindKey(settings.command_line_options, name)) { + fn(SettingsSpan(*values), Source::COMMAND_LINE); + } + // Merge in the read-write settings + if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { + fn(SettingsSpan(*value), Source::RW_SETTINGS); + } + // Merge in the network-specific section of the config file + if (!section.empty()) { + if (auto* map = FindKey(settings.ro_config, section)) { + if (auto* values = FindKey(*map, name)) { + fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); + } + } + } + // Merge in the default section of the config file + if (auto* map = FindKey(settings.ro_config, "")) { + if (auto* values = FindKey(*map, name)) { + fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); + } + } +} +} // namespace + +bool ReadSettings(const fs::path& path, std::map& values, std::vector& errors) +{ + values.clear(); + errors.clear(); + + fsbridge::ifstream file; + file.open(path); + if (!file.is_open()) return true; // Ok for file not to exist. + + SettingsValue in; + if (!in.read(std::string{std::istreambuf_iterator(file), std::istreambuf_iterator()})) { + errors.emplace_back(strprintf("Unable to parse settings file %s", path.string())); + return false; + } + + if (file.fail()) { + errors.emplace_back(strprintf("Failed reading settings file %s", path.string())); + return false; + } + file.close(); // Done with file descriptor. Release while copying data. + + if (!in.isObject()) { + errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string())); + return false; + } + + const std::vector& in_keys = in.getKeys(); + const std::vector& in_values = in.getValues(); + for (size_t i = 0; i < in_keys.size(); ++i) { + auto inserted = values.emplace(in_keys[i], in_values[i]); + if (!inserted.second) { + errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string())); + } + } + return errors.empty(); +} + +bool WriteSettings(const fs::path& path, + const std::map& values, + std::vector& errors) +{ + SettingsValue out(SettingsValue::VOBJ); + for (const auto& value : values) { + out.__pushKV(value.first, value.second); + } + fsbridge::ofstream file; + file.open(path); + if (file.fail()) { + errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string())); + return false; + } + file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl; + file.close(); + return true; +} + +SettingsValue GetSetting(const Settings& settings, + const std::string& section, + const std::string& name, + bool ignore_default_section_config, + bool get_chain_name) +{ + SettingsValue result; + bool done = false; // Done merging any more settings sources. + MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { + // Weird behavior preserved for backwards compatibility: Apply negated + // setting even if non-negated setting would be ignored. A negated + // value in the default section is applied to network specific options, + // even though normal non-negated values there would be ignored. + const bool never_ignore_negated_setting = span.last_negated(); + + // Weird behavior preserved for backwards compatibility: Take first + // assigned value instead of last. In general, later settings take + // precedence over early settings, but for backwards compatibility in + // the config file the precedence is reversed for all settings except + // chain name settings. + const bool reverse_precedence = + (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && + !get_chain_name; + + // Weird behavior preserved for backwards compatibility: Negated + // -regtest and -testnet arguments which you would expect to override + // values set in the configuration file are currently accepted but + // silently ignored. It would be better to apply these just like other + // negated values, or at least warn they are ignored. + const bool skip_negated_command_line = get_chain_name; + + if (done) return; + + // Ignore settings in default config section if requested. + if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION && + !never_ignore_negated_setting) { + return; + } + + // Skip negated command line settings. + if (skip_negated_command_line && span.last_negated()) return; + + if (!span.empty()) { + result = reverse_precedence ? span.begin()[0] : span.end()[-1]; + done = true; + } else if (span.last_negated()) { + result = false; + done = true; + } + }); + return result; +} + +std::vector GetSettingsList(const Settings& settings, + const std::string& section, + const std::string& name, + bool ignore_default_section_config) +{ + std::vector result; + bool done = false; // Done merging any more settings sources. + bool prev_negated_empty = false; + MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { + // Weird behavior preserved for backwards compatibility: Apply config + // file settings even if negated on command line. Negating a setting on + // command line will ignore earlier settings on the command line and + // ignore settings in the config file, unless the negated command line + // value is followed by non-negated value, in which case config file + // settings will be brought back from the dead (but earlier command + // line settings will still be ignored). + const bool add_zombie_config_values = + (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && + !prev_negated_empty; + + // Ignore settings in default config section if requested. + if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return; + + // Add new settings to the result if isn't already complete, or if the + // values are zombies. + if (!done || add_zombie_config_values) { + for (const auto& value : span) { + if (value.isArray()) { + result.insert(result.end(), value.getValues().begin(), value.getValues().end()); + } else { + result.push_back(value); + } + } + } + + // If a setting was negated, or if a setting was forced, set + // done to true to ignore any later lower priority settings. + done |= span.negated() > 0 || source == Source::FORCED; + + // Update the negated and empty state used for the zombie values check. + prev_negated_empty |= span.last_negated() && result.empty(); + }); + return result; +} + +bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name) +{ + bool has_default_section_setting = false; + bool has_other_setting = false; + MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { + if (span.empty()) return; + else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true; + else has_other_setting = true; + }); + // If a value is set in the default section and not explicitly overwritten by the + // user on the command line or in a different section, then we want to enable + // warnings about the value being ignored. + return has_default_section_setting && !has_other_setting; +} + +SettingsSpan::SettingsSpan(const std::vector& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {} +const SettingsValue* SettingsSpan::begin() const { return data + negated(); } +const SettingsValue* SettingsSpan::end() const { return data + size; } +bool SettingsSpan::empty() const { return size == 0 || last_negated(); } +bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); } +size_t SettingsSpan::negated() const +{ + for (size_t i = size; i > 0; --i) { + if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value) + } + return 0; +} + +} // namespace util diff --git a/src/util/settings.h b/src/util/settings.h new file mode 100644 index 0000000000..ed36349232 --- /dev/null +++ b/src/util/settings.h @@ -0,0 +1,108 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_SETTINGS_H +#define BITCOIN_UTIL_SETTINGS_H + +#include + +#include +#include +#include + +class UniValue; + +namespace util { + +//! Settings value type (string/integer/boolean/null variant). +//! +//! @note UniValue is used here for convenience and because it can be easily +//! serialized in a readable format. But any other variant type that can +//! be assigned strings, int64_t, and bool values and has get_str(), +//! get_int64(), get_bool(), isNum(), isBool(), isFalse(), isTrue() and +//! isNull() methods can be substituted if there's a need to move away +//! from UniValue. (An implementation with boost::variant was posted at +//! https://github.com/bitcoin/bitcoin/pull/15934/files#r337691812) +using SettingsValue = UniValue; + +//! Stored settings. This struct combines settings from the command line, a +//! read-only configuration file, and a read-write runtime settings file. +struct Settings { + //! Map of setting name to forced setting value. + std::map forced_settings; + //! Map of setting name to list of command line values. + std::map> command_line_options; + //! Map of setting name to read-write file setting value. + std::map rw_settings; + //! Map of config section name and setting name to list of config file values. + std::map>> ro_config; +}; + +//! Read settings file. +bool ReadSettings(const fs::path& path, + std::map& values, + std::vector& errors); + +//! Write settings file. +bool WriteSettings(const fs::path& path, + const std::map& values, + std::vector& errors); + +//! Get settings value from combined sources: forced settings, command line +//! arguments, runtime read-write settings, and the read-only config file. +//! +//! @param ignore_default_section_config - ignore values in the default section +//! of the config file (part before any +//! [section] keywords) +//! @param get_chain_name - enable special backwards compatible behavior +//! for GetChainName +SettingsValue GetSetting(const Settings& settings, + const std::string& section, + const std::string& name, + bool ignore_default_section_config, + bool get_chain_name); + +//! Get combined setting value similar to GetSetting(), except if setting was +//! specified multiple times, return a list of all the values specified. +std::vector GetSettingsList(const Settings& settings, + const std::string& section, + const std::string& name, + bool ignore_default_section_config); + +//! Return true if a setting is set in the default config file section, and not +//! overridden by a higher priority command-line or network section value. +//! +//! This is used to provide user warnings about values that might be getting +//! ignored unintentionally. +bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name); + +//! Accessor for list of settings that skips negated values when iterated over. +//! The last boolean `false` value in the list and all earlier values are +//! considered negated. +struct SettingsSpan { + explicit SettingsSpan() = default; + explicit SettingsSpan(const SettingsValue& value) noexcept : SettingsSpan(&value, 1) {} + explicit SettingsSpan(const SettingsValue* data, size_t size) noexcept : data(data), size(size) {} + explicit SettingsSpan(const std::vector& vec) noexcept; + const SettingsValue* begin() const; //!< Pointer to first non-negated value. + const SettingsValue* end() const; //!< Pointer to end of values. + bool empty() const; //!< True if there are any non-negated values. + bool last_negated() const; //!< True if the last value is negated. + size_t negated() const; //!< Number of negated values. + + const SettingsValue* data = nullptr; + size_t size = 0; +}; + +//! Map lookup helper. +template +auto FindKey(Map&& map, Key&& key) -> decltype(&map.at(key)) +{ + auto it = map.find(key); + return it == map.end() ? nullptr : &it->second; +} + +} // namespace util + +#endif // BITCOIN_UTIL_SETTINGS_H diff --git a/src/util/string.cpp b/src/util/string.cpp new file mode 100644 index 0000000000..86ec803df2 --- /dev/null +++ b/src/util/string.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +using namespace std; + +void ParseString(const string& str, char c, vector& v) +{ + if (str.empty()) + return; + string::size_type i1 = 0; + string::size_type i2; + while (true) + { + i2 = str.find(c, i1); + if (i2 == str.npos) + { + v.push_back(str.substr(i1)); + return; + } + v.push_back(str.substr(i1, i2-i1)); + i1 = i2+1; + } +} diff --git a/src/util/string.h b/src/util/string.h new file mode 100644 index 0000000000..113e4d1d8d --- /dev/null +++ b/src/util/string.h @@ -0,0 +1,100 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_STRING_H +#define BITCOIN_UTIL_STRING_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +void ParseString(const std::string& str, char c, std::vector& v); + +[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v") +{ + std::string::size_type front = str.find_first_not_of(pattern); + if (front == std::string::npos) { + return std::string(); + } + std::string::size_type end = str.find_last_not_of(pattern); + return str.substr(front, end - front + 1); +} + +[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix) +{ + if (str.substr(0, prefix.size()) == prefix) { + return str.substr(prefix.size()); + } + return str; +} + +/** + * Join a list of items + * + * @param list The list to join + * @param separator The separator + * @param unary_op Apply this operator to each item in the list + */ +template +auto Join(const std::vector& list, const BaseType& separator, UnaryOp unary_op) + -> decltype(unary_op(list.at(0))) +{ + decltype(unary_op(list.at(0))) ret; + for (size_t i = 0; i < list.size(); ++i) { + if (i > 0) ret += separator; + ret += unary_op(list.at(i)); + } + return ret; +} + +template +T Join(const std::vector& list, const T& separator) +{ + return Join(list, separator, [](const T& i) { return i; }); +} + +// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above. +inline std::string Join(const std::vector& list, const std::string& separator) +{ + return Join(list, separator); +} + +/** + * Check if a string does not contain any embedded NUL (\0) characters + */ +[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept +{ + return str.size() == strlen(str.c_str()); +} + +/** + * Locale-independent version of std::to_string + */ +template +std::string ToString(const T& t) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << t; + return oss.str(); +} + +/** + * Check whether a container begins with the given prefix. + */ +template +[[nodiscard]] inline bool HasPrefix(const T1& obj, + const std::array& prefix) +{ + return obj.size() >= PREFIX_LEN && + std::equal(std::begin(prefix), std::end(prefix), std::begin(obj)); +} + +#endif // BITCOIN_UTIL_STRENCODINGS_H diff --git a/src/util/system.cpp b/src/util/system.cpp new file mode 100644 index 0000000000..fc572b6ed7 --- /dev/null +++ b/src/util/system.cpp @@ -0,0 +1,1141 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#ifdef WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4804) +#pragma warning(disable:4805) +#pragma warning(disable:4717) +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include /* for _commit */ +#include +#include "shlobj.h" +#elif defined(__linux__) +# include +#endif + +#ifdef HAVE_MALLOPT_ARENA_MAX +#include +#endif + +const char * const GRIDCOIN_CONF_FILENAME = "gridcoinresearch.conf"; +const char * const GRIDCOIN_SETTINGS_FILENAME = "gridcoinsettings.json"; + +std::string strMiscWarning; + +ArgsManager gArgs; + +/** + * Interpret a string argument as a boolean. + * + * The definition of atoi() requires that non-numeric string values like "foo", + * return 0. This means that if a user unintentionally supplies a non-integer + * argument here, the return value is always false. This means that -foo=false + * does what the user probably expects, but -foo=true is well defined but does + * not do what they probably expected. + * + * The return value of atoi() is undefined when given input not representable as + * an int. On most systems this means string value between "-2147483648" and + * "2147483647" are well defined (this method will return true). Setting + * -txindex=2147483648 on most systems, however, is probably undefined. + * + * For a more extensive discussion of this topic (and a wide range of opinions + * on the Right Way to change this code), see PR12713. + */ +static bool InterpretBool(const std::string& strValue) +{ + if (strValue.empty()) + return true; + return (atoi(strValue) != 0); +} + +static std::string SettingName(const std::string& arg) +{ + return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg; +} + +/** + * Interpret -nofoo as if the user supplied -foo=0. + * + * This method also tracks when the -no form was supplied, and if so, + * checks whether there was a double-negative (-nofoo=0 -> -foo=1). + * + * If there was not a double negative, it removes the "no" from the key + * and returns false. + * + * If there was a double negative, it removes "no" from the key, and + * returns true. + * + * If there was no "no", it returns the string value untouched. + * + * Where an option was negated can be later checked using the + * IsArgNegated() method. One use case for this is to have a way to disable + * options that are not normally boolean (e.g. using -nodebuglogfile to request + * that debug log output is not sent to any file at all). + */ + +static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value) +{ + // Split section name from key name for keys like "testnet.foo" or "regtest.bar" + size_t option_index = key.find('.'); + if (option_index != std::string::npos) { + section = key.substr(0, option_index); + key.erase(0, option_index + 1); + } + if (key.substr(0, 2) == "no") { + key.erase(0, 2); + // Double negatives like -nofoo=0 are supported (but discouraged) + if (!InterpretBool(value)) { + LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value); + return true; + } + return false; + } + return value; +} + +/** + * Check settings value validity according to flags. + * + * TODO: Add more meaningful error checks here in the future + * See "here's how the flags are meant to behave" in + * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823 + */ +static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error) +{ + if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) { + error = strprintf("Negating of -%s is meaningless and therefore forbidden", key); + return false; + } + return true; +} + +namespace { +fs::path StripRedundantLastElementsOfPath(const fs::path& path) +{ + auto result = path; + while (result.filename().string() == ".") { + result = result.parent_path(); + } + + assert(fs::equivalent(result, path)); + return result; +} +} // namespace + +// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to +// #include class definitions for all members. +// For example, m_settings has an internal dependency on univalue. +ArgsManager::ArgsManager() {} +ArgsManager::~ArgsManager() {} + +const std::set ArgsManager::GetUnsuitableSectionOnlyArgs() const +{ + std::set unsuitables; + + LOCK(cs_args); + + // if there's no section selected, don't worry + if (m_network.empty()) return std::set {}; + + // if it's okay to use the default section for this network, don't worry + if (m_network == CBaseChainParams::MAIN) return std::set {}; + + for (const auto& arg : m_network_only_args) { + if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) { + unsuitables.insert(arg); + } + } + return unsuitables; +} + +const std::list ArgsManager::GetUnrecognizedSections() const +{ + // Section names to be recognized in the config file. + static const std::set available_sections{ + CBaseChainParams::TESTNET, + CBaseChainParams::MAIN + }; + + LOCK(cs_args); + std::list unrecognized = m_config_sections; + unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); }); + return unrecognized; +} + +void ArgsManager::SelectConfigNetwork(const std::string& network) +{ + LOCK(cs_args); + m_network = network; +} + +bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error) +{ + LOCK(cs_args); + m_settings.command_line_options.clear(); + + for (int i = 1; i < argc; i++) { + std::string key(argv[i]); + +#ifdef MAC_OSX + // At the first time when a user gets the "App downloaded from the + // internet" warning, and clicks the Open button, macOS passes + // a unique process serial number (PSN) as -psn_... command-line + // argument, which we filter out. + if (key.substr(0, 5) == "-psn_") continue; +#endif + + if (key == "-") break; //bitcoin-tx using stdin + std::string val; + size_t is_index = key.find('='); + if (is_index != std::string::npos) { + val = key.substr(is_index + 1); + key.erase(is_index); + } +#ifdef WIN32 + key = ToLower(key); + if (key[0] == '/') + key[0] = '-'; +#endif + + if (key[0] != '-') { + if (!m_accept_any_command && m_command.empty()) { + // The first non-dash arg is a registered command + std::optional flags = GetArgFlags(key); + if (!flags || !(*flags & ArgsManager::COMMAND)) { + error = strprintf("Invalid command '%s'", argv[i]); + return false; + } + } + m_command.push_back(key); + while (++i < argc) { + // The remaining args are command args + m_command.push_back(argv[i]); + } + break; + } + + // Transform --foo to -foo + if (key.length() > 1 && key[1] == '-') + key.erase(0, 1); + + // Transform -foo to foo + key.erase(0, 1); + std::string section; + util::SettingsValue value = InterpretOption(section, key, val); + std::optional flags = GetArgFlags('-' + key); + + // Unknown command line options and command line options with dot + // characters (which are returned from InterpretOption with nonempty + // section strings) are not valid. + if (!flags || !section.empty()) { + error = strprintf("Invalid parameter %s", argv[i]); + return false; + } + + if (!CheckValid(key, value, *flags, error)) return false; + + m_settings.command_line_options[key].push_back(value); + } + + // we do not allow -includeconf from command line + bool success = true; + if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) { + for (const auto& include : util::SettingsSpan(*includes)) { + error += "-includeconf cannot be used from commandline; -includeconf=" + include.get_str() + "\n"; + success = false; + } + } + return success; +} + +std::optional ArgsManager::GetArgFlags(const std::string& name) const +{ + LOCK(cs_args); + for (const auto& arg_map : m_available_args) { + const auto search = arg_map.second.find(name); + if (search != arg_map.second.end()) { + return search->second.m_flags; + } + } + return std::nullopt; +} + +const fs::path& ArgsManager::GetBlocksDirPath() +{ + LOCK(cs_args); + fs::path& path = m_cached_blocks_path; + + // Cache the path to avoid calling fs::create_directories on every call of + // this function + if (!path.empty()) return path; + + if (IsArgSet("-blocksdir")) { + path = fs::system_complete(GetArg("-blocksdir", "")); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDataDirPath(false); + } + + path /= BaseParams().DataDir(); + path /= "blocks"; + fs::create_directories(path); + path = StripRedundantLastElementsOfPath(path); + return path; +} + +const fs::path& ArgsManager::GetDataDirPath(bool net_specific) const +{ + LOCK(cs_args); + fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path; + + // Cache the path to avoid calling fs::create_directories on every call of + // this function + if (!path.empty()) return path; + + std::string datadir = GetArg("-datadir", ""); + if (!datadir.empty()) { + path = fs::system_complete(datadir); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDefaultDataDir(); + } + if (net_specific) + path /= BaseParams().DataDir(); + + /* Reserved for future Bitcoin backport functionality with wallets. + if (fs::create_directories(path)) { + // This is the first run, create wallets subdirectory too + fs::create_directories(path / "wallets"); + } + */ + + path = StripRedundantLastElementsOfPath(path); + return path; +} + +void ArgsManager::ClearPathCache() +{ + LOCK(cs_args); + + m_cached_datadir_path = fs::path(); + m_cached_network_datadir_path = fs::path(); + m_cached_blocks_path = fs::path(); +} + +std::optional ArgsManager::GetCommand() const +{ + Command ret; + LOCK(cs_args); + auto it = m_command.begin(); + if (it == m_command.end()) { + // No command was passed + return std::nullopt; + } + if (!m_accept_any_command) { + // The registered command + ret.command = *(it++); + } + while (it != m_command.end()) { + // The unregistered command and args (if any) + ret.args.push_back(*(it++)); + } + return ret; +} + +std::vector ArgsManager::GetArgs(const std::string& strArg) const +{ + std::vector result; + for (const util::SettingsValue& value : GetSettingsList(strArg)) { + result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str()); + } + return result; +} + +bool ArgsManager::IsArgSet(const std::string& strArg) const +{ + return !GetSetting(strArg).isNull(); +} + +bool ArgsManager::InitSettings(std::string& error) +{ + if (!GetSettingsPath()) { + return true; // Do nothing if settings file disabled. + } + + std::vector errors; + if (!ReadSettingsFile(&errors)) { + error = strprintf("Failed loading settings file:\n- %s\n", Join(errors, "\n- ")); + return false; + } + if (!WriteSettingsFile(&errors)) { + error = strprintf("Failed saving settings file:\n- %s\n", Join(errors, "\n- ")); + return false; + } + return true; +} + +bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const +{ + if (IsArgNegated("-settings")) { + return false; + } + if (filepath) { + std::string settings = GetArg("-settings", GRIDCOIN_SETTINGS_FILENAME); + *filepath = fsbridge::AbsPathJoin(GetDataDirPath(/* net_specific= */ true), temp ? settings + ".tmp" : settings); + } + return true; +} + +static void SaveErrors(const std::vector errors, std::vector* error_out) +{ + for (const auto& error : errors) { + if (error_out) { + error_out->emplace_back(error); + } else { + LogPrintf("%s\n", error); + } + } +} + +bool ArgsManager::ReadSettingsFile(std::vector* errors) +{ + fs::path path; + if (!GetSettingsPath(&path, /* temp= */ false)) { + return true; // Do nothing if settings file disabled. + } + + LOCK(cs_args); + m_settings.rw_settings.clear(); + std::vector read_errors; + if (!util::ReadSettings(path, m_settings.rw_settings, read_errors)) { + SaveErrors(read_errors, errors); + return false; + } + for (const auto& setting : m_settings.rw_settings) { + std::string section; + std::string key = setting.first; + (void)InterpretOption(section, key, /* value */ {}); // Split setting key into section and argname + if (!GetArgFlags('-' + key)) { + LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first); + } + } + return true; +} + +bool ArgsManager::WriteSettingsFile(std::vector* errors) const +{ + fs::path path, path_tmp; + if (!GetSettingsPath(&path, /* temp= */ false) || !GetSettingsPath(&path_tmp, /* temp= */ true)) { + throw std::logic_error("Attempt to write settings file when dynamic settings are disabled."); + } + + LOCK(cs_args); + std::vector write_errors; + if (!util::WriteSettings(path_tmp, m_settings.rw_settings, write_errors)) { + SaveErrors(write_errors, errors); + return false; + } + if (!RenameOver(path_tmp, path)) { + SaveErrors({strprintf("Failed renaming settings file %s to %s\n", path_tmp.string(), path.string())}, errors); + return false; + } + return true; +} + +bool ArgsManager::IsArgNegated(const std::string& strArg) const +{ + return GetSetting(strArg).isFalse(); +} + +std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const +{ + const util::SettingsValue value = GetSetting(strArg); + return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str(); +} + +int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const +{ + const util::SettingsValue value = GetSetting(strArg); + return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str()); +} + +bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const +{ + const util::SettingsValue value = GetSetting(strArg); + return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str()); +} + +bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue) +{ + LOCK(cs_args); + if (IsArgSet(strArg)) return false; + ForceSetArg(strArg, strValue); + return true; +} + +bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue) +{ + if (fValue) + return SoftSetArg(strArg, std::string("1")); + else + return SoftSetArg(strArg, std::string("0")); +} + +void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue) +{ + LOCK(cs_args); + m_settings.forced_settings[SettingName(strArg)] = strValue; +} + +void ArgsManager::AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat) +{ + Assert(cmd.find('=') == std::string::npos); + Assert(cmd.at(0) != '-'); + + LOCK(cs_args); + m_accept_any_command = false; // latch to false + std::map& arg_map = m_available_args[cat]; + auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND}); + Assert(ret.second); // Fail on duplicate commands +} + +void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat) +{ + Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand + + // Split arg name from its help param + size_t eq_index = name.find('='); + if (eq_index == std::string::npos) { + eq_index = name.size(); + } + std::string arg_name = name.substr(0, eq_index); + + LOCK(cs_args); + std::map& arg_map = m_available_args[cat]; + auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags}); + assert(ret.second); // Make sure an insertion actually happened + + if (flags & ArgsManager::NETWORK_ONLY) { + m_network_only_args.emplace(arg_name); + } +} + +void ArgsManager::AddHiddenArgs(const std::vector& names) +{ + for (const std::string& name : names) { + AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); + } +} + +std::string ArgsManager::GetHelpMessage() const +{ + const bool show_debug = GetBoolArg("-help-debug", false); + + std::string usage = ""; + LOCK(cs_args); + for (const auto& arg_map : m_available_args) { + switch(arg_map.first) { + case OptionsCategory::OPTIONS: + usage += HelpMessageGroup("Options:"); + break; + case OptionsCategory::CONNECTION: + usage += HelpMessageGroup("Connection options:"); + break; + case OptionsCategory::ZMQ: + usage += HelpMessageGroup("ZeroMQ notification options:"); + break; + case OptionsCategory::DEBUG_TEST: + usage += HelpMessageGroup("Debugging/Testing options:"); + break; + case OptionsCategory::NODE_RELAY: + usage += HelpMessageGroup("Node relay options:"); + break; + case OptionsCategory::BLOCK_CREATION: + usage += HelpMessageGroup("Block creation options:"); + break; + case OptionsCategory::RPC: + usage += HelpMessageGroup("RPC server options:"); + break; + case OptionsCategory::WALLET: + usage += HelpMessageGroup("Wallet options:"); + break; + case OptionsCategory::WALLET_DEBUG_TEST: + if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:"); + break; + case OptionsCategory::CHAINPARAMS: + usage += HelpMessageGroup("Chain selection options:"); + break; + case OptionsCategory::GUI: + usage += HelpMessageGroup("UI Options:"); + break; + case OptionsCategory::COMMANDS: + usage += HelpMessageGroup("Commands:"); + break; + case OptionsCategory::REGISTER_COMMANDS: + usage += HelpMessageGroup("Register Commands:"); + break; + case OptionsCategory::SCRAPER: + usage += HelpMessageGroup("Scraper options:"); + break; + case OptionsCategory::RESEARCHER: + usage += HelpMessageGroup("Researcher options:"); + break; + default: + break; + } + + // When we get to the hidden options, stop + if (arg_map.first == OptionsCategory::HIDDEN) break; + + for (const auto& arg : arg_map.second) { + if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) { + std::string name; + if (arg.second.m_help_param.empty()) { + name = arg.first; + } else { + name = arg.first + arg.second.m_help_param; + } + usage += HelpMessageOpt(name, arg.second.m_help_text); + } + } + } + return usage; +} + +bool HelpRequested(const ArgsManager& args) +{ + return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug"); +} + +void SetupHelpOptions(ArgsManager& args) +{ + args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + args.AddHiddenArgs({"-h", "-help"}); +} + +static const int screenWidth = 79; +static const int optIndent = 2; +static const int msgIndent = 7; + +std::string HelpMessageGroup(const std::string &message) { + return std::string(message) + std::string("\n\n"); +} + +std::string HelpMessageOpt(const std::string &option, const std::string &message) { + return std::string(optIndent,' ') + std::string(option) + + std::string("\n") + std::string(msgIndent,' ') + + FormatParagraph(message, screenWidth - msgIndent, msgIndent) + + std::string("\n\n"); +} + +static std::string FormatException(std::exception* pex, const char* pszThread) +{ +#ifdef WIN32 + char pszModule[MAX_PATH] = ""; + GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); +#else + const char* pszModule = "gridcoin"; +#endif + if (pex) + return strprintf( + "EXCEPTION: %s \n%s \n%s in %s\n", typeid(*pex).name(), pex->what(), pszModule, pszThread); + else + return strprintf( + "UNKNOWN EXCEPTION \n%s in %s\n", pszModule, pszThread); +} + +void LogException(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + LogPrintf("%s", message); +} + +void PrintException(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + LogPrintf("\n\n************************\n%s", message); + fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + strMiscWarning = message; + throw; +} + +void PrintExceptionContinue(std::exception* pex, const char* pszThread) +{ + std::string message = FormatException(pex, pszThread); + LogPrintf("\n\n************************\n%s", message); + fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); + strMiscWarning = message; +} + +fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) +{ + if (path.is_absolute()) { + return path; + } + return fsbridge::AbsPathJoin(GetDataDir(net_specific), path); +} + +fs::path GetDefaultDataDir() +{ + // Windows < Vista: C:\Documents and Settings\Username\Application Data\GridcoinResearch + // Windows >= Vista: C:\Users\Username\AppData\Roaming\GridcoinResearch + // Mac: ~/Library/Application Support/GridcoinResearch + // Linux/Unix: ~/.GridcoinResearch +#ifdef WIN32 + // Windows + + // This is the user's base roaming AppData path with GridcoinResearch added. + return GetSpecialFolderPath(CSIDL_APPDATA) / "GridcoinResearch"; +#else + fs::path pathRet; + + // For everything except for Windows, use the environment variable to get + // the home path. + char* pszHome = getenv("HOME"); + + // There is no home path, so default to the root directory. + if (pszHome == nullptr || strlen(pszHome) == 0) { + pathRet = fs::path("/"); + } else { + pathRet = fs::path(pszHome); + } +#ifdef MAC_OSX + // The pathRet here represents the HOME directory. Apple + // applications are expected to store their files in + // "~/Library/Application Support/[AppDir]. + return pathRet / "Library" / "Application Support" / "GridcoinResearch"; +#else + // Linux/Unix + return pathRet / ".GridcoinResearch"; +#endif // MAC_OSX +#endif // WIN32 +} + +const fs::path &GetDataDir(bool fNetSpecific) +{ + return gArgs.GetDataDirPath(fNetSpecific); +} + +bool CheckDataDirOption() +{ + std::string datadir = gArgs.GetArg("-datadir", ""); + return datadir.empty() || fs::is_directory(fs::system_complete(datadir)); +} + +fs::path GetConfigFile(const std::string& confPath) +{ + // Unlike in Bitcoin, the net specific flag is TRUE, because we still use split config files. + return AbsPathForConfigVal(fs::path(confPath), true); +} + +fs::path GetConfigFile() +{ + return AbsPathForConfigVal(gArgs.GetArg("-conf", GRIDCOIN_CONF_FILENAME), true); +} + +bool IsConfigFileEmpty() +{ + fsbridge::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + { + return true; + } + return false; + +} + +static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, + std::vector>& options, std::list& sections) +{ + std::string str, prefix; + std::string::size_type pos; + int linenr = 1; + while (std::getline(stream, str)) { + bool used_hash = false; + if ((pos = str.find('#')) != std::string::npos) { + str = str.substr(0, pos); + used_hash = true; + } + const static std::string pattern = " \t\r\n"; + str = TrimString(str, pattern); + if (!str.empty()) { + if (*str.begin() == '[' && *str.rbegin() == ']') { + const std::string section = str.substr(1, str.size() - 2); + sections.emplace_back(SectionInfo{section, filepath, linenr}); + prefix = section + '.'; + } else if (*str.begin() == '-') { + error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str); + return false; + } else if ((pos = str.find('=')) != std::string::npos) { + std::string name = prefix + TrimString(str.substr(0, pos), pattern); + std::string value = TrimString(str.substr(pos + 1), pattern); + if (used_hash && name.find("rpcpassword") != std::string::npos) { + error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr); + return false; + } + options.emplace_back(name, value); + if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) { + sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr}); + } + } else { + error = strprintf("parse error on line %i: %s", linenr, str); + if (str.size() >= 2 && str.substr(0, 2) == "no") { + error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str); + } + return false; + } + } + ++linenr; + } + return true; +} + +bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys) +{ + LOCK(cs_args); + std::vector> options; + if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) { + return false; + } + for (const std::pair& option : options) { + std::string section; + std::string key = option.first; + util::SettingsValue value = InterpretOption(section, key, option.second); + std::optional flags = GetArgFlags('-' + key); + if (flags) { + if (!CheckValid(key, value, *flags, error)) { + return false; + } + m_settings.ro_config[section][key].push_back(value); + } else { + if (ignore_invalid_keys) { + LogPrintf("Ignoring unknown configuration value %s\n", option.first); + } else { + error = strprintf("Invalid configuration value %s", option.first); + return false; + } + } + } + return true; +} + +bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) +{ + { + LOCK(cs_args); + m_settings.ro_config.clear(); + m_config_sections.clear(); + } + + const std::string confPath = GetArg("-conf", GRIDCOIN_CONF_FILENAME); + + fsbridge::ifstream stream(GetConfigFile(confPath)); + + // ok to not have a config file + if (stream.good()) { + if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) { + return false; + } + // `-includeconf` cannot be included in the command line arguments except + // as `-noincludeconf` (which indicates that no included conf file should be used). + bool use_conf_file{true}; + { + LOCK(cs_args); + if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) { + // ParseParameters() fails if a non-negated -includeconf is passed on the command-line + assert(util::SettingsSpan(*includes).last_negated()); + use_conf_file = false; + } + } + if (use_conf_file) { + std::string chain_id = GetChainName(); + std::vector conf_file_names; + + auto add_includes = [&](const std::string& network, size_t skip = 0) { + size_t num_values = 0; + LOCK(cs_args); + if (auto* section = util::FindKey(m_settings.ro_config, network)) { + if (auto* values = util::FindKey(*section, "includeconf")) { + for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) { + conf_file_names.push_back((*values)[i].get_str()); + } + num_values = values->size(); + } + } + return num_values; + }; + + // We haven't set m_network yet (that happens in SelectParams()), so manually check + // for network.includeconf args. + const size_t chain_includes = add_includes(chain_id); + const size_t default_includes = add_includes({}); + + for (const std::string& conf_file_name : conf_file_names) { + fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name)); + if (conf_file_stream.good()) { + if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) { + return false; + } + LogPrintf("Included configuration file %s\n", conf_file_name); + } else { + error = "Failed to include configuration file " + conf_file_name; + return false; + } + } + + // Warn about recursive -includeconf + conf_file_names.clear(); + add_includes(chain_id, /* skip= */ chain_includes); + add_includes({}, /* skip= */ default_includes); + std::string chain_id_final = GetChainName(); + if (chain_id_final != chain_id) { + // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs + add_includes(chain_id_final); + } + for (const std::string& conf_file_name : conf_file_names) { + tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name); + } + } + } + + // If datadir is changed in .conf file: + gArgs.ClearPathCache(); + if (!CheckDataDirOption()) { + error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", "")); + return false; + } + return true; +} + +std::string ArgsManager::GetChainName() const +{ + auto get_net = [&](const std::string& arg) { + LOCK(cs_args); + util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg), + /* ignore_default_section_config= */ false, + /* get_chain_name= */ true); + return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str()); + }; + + const bool fRegTest = get_net("-regtest"); + const bool fSigNet = get_net("-signet"); + const bool fTestNet = get_net("-testnet"); + const bool is_chain_arg_set = IsArgSet("-chain"); + + if ((int)is_chain_arg_set + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) { + throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one."); + } + if (fTestNet) return CBaseChainParams::TESTNET; + + return GetArg("-chain", CBaseChainParams::MAIN); +} + +bool ArgsManager::UseDefaultSection(const std::string& arg) const +{ + return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0; +} + +util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const +{ + LOCK(cs_args); + return util::GetSetting( + m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), /* get_chain_name= */ false); +} + +std::vector ArgsManager::GetSettingsList(const std::string& arg) const +{ + LOCK(cs_args); + return util::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg)); +} + +void ArgsManager::logArgsPrefix( + const std::string& prefix, + const std::string& section, + const std::map>& args) const +{ + std::string section_str = section.empty() ? "" : "[" + section + "] "; + for (const auto& arg : args) { + for (const auto& value : arg.second) { + std::optional flags = GetArgFlags('-' + arg.first); + if (flags) { + std::string value_str = (*flags & SENSITIVE) ? "****" : value.write(); + LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str); + } + } + } +} + +void ArgsManager::LogArgs() const +{ + LOCK(cs_args); + for (const auto& section : m_settings.ro_config) { + logArgsPrefix("Config file arg:", section.first, section.second); + } + for (const auto& setting : m_settings.rw_settings) { + LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write()); + } + logArgsPrefix("Command-line arg:", "", m_settings.command_line_options); +} + +bool RenameOver(fs::path src, fs::path dest) +{ +#ifdef WIN32 + return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(), + MOVEFILE_REPLACE_EXISTING) != 0; +#else + int rc = std::rename(src.string().c_str(), dest.string().c_str()); + return (rc == 0); +#endif /* WIN32 */ +} + +void SetupEnvironment() +{ +#ifdef HAVE_MALLOPT_ARENA_MAX + // glibc-specific: On 32-bit systems set the number of arenas to 1. + // By default, since glibc 2.10, the C library will create up to two heap + // arenas per core. This is known to cause excessive virtual address space + // usage in our usage. Work around it by setting the maximum number of + // arenas to 1. + if (sizeof(void*) == 4) { + mallopt(M_ARENA_MAX, 1); + } +#endif + // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale + // may be invalid, in which case the "C.UTF-8" locale is used as fallback. +#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) + try { + std::locale(""); // Raises a runtime error if current locale is invalid + } catch (const std::runtime_error&) { + setenv("LC_ALL", "C.UTF-8", 1); + } +#elif defined(WIN32) + // Set the default input/output charset is utf-8 + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); +#endif + // The path locale is lazy initialized and to avoid deinitialization errors + // in multithreading environments, it is set explicitly by the main thread. + // A dummy locale is used to extract the internal default locale, used by + // fs::path, which is then used to explicitly imbue the path. + std::locale loc = fs::path::imbue(std::locale::classic()); +#ifndef WIN32 + fs::path::imbue(loc); +#else + fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16())); +#endif +} + +// Newer FileCommit overload from Bitcoin. +bool FileCommit(FILE *file) +{ + if (fflush(file) != 0) { // harmless if redundantly called + LogPrintf("%s: fflush failed: %d\n", __func__, errno); + return false; + } +#ifdef WIN32 + HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); + if (FlushFileBuffers(hFile) == 0) { + LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); + return false; + } +#else + #if defined(__linux__) || defined(__NetBSD__) + if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync + LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); + return false; + } + #elif defined(MAC_OSX) && defined(F_FULLFSYNC) + if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success + LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); + return false; + } + #else + if (fsync(fileno(file)) != 0 && errno != EINVAL) { + LogPrintf("%s: fsync failed: %d\n", __func__, errno); + return false; + } + #endif +#endif + return true; +} + +#ifdef WIN32 +fs::path GetSpecialFolderPath(int nFolder, bool fCreate) +{ + wchar_t pszPath[MAX_PATH] = L""; + + if (SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) + { + return fs::path(pszPath); + } + + LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path."); + return fs::path(""); +} +#endif + +namespace util { +#ifdef WIN32 +WinCmdLineArgs::WinCmdLineArgs() +{ + wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + std::wstring_convert, wchar_t> utf8_cvt; + argv = new char*[argc]; + args.resize(argc); + for (int i = 0; i < argc; i++) { + args[i] = utf8_cvt.to_bytes(wargv[i]); + argv[i] = &*args[i].begin(); + } + LocalFree(wargv); +} + +WinCmdLineArgs::~WinCmdLineArgs() +{ + delete[] argv; +} + +std::pair WinCmdLineArgs::get() +{ + return std::make_pair(argc, argv); +} +#endif +} // namespace util + + diff --git a/src/util/system.h b/src/util/system.h new file mode 100644 index 0000000000..b75b0295aa --- /dev/null +++ b/src/util/system.h @@ -0,0 +1,467 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/** + * Server/client environment: argument handling, config file parsing, + * thread wrappers, startup time + */ +#ifndef BITCOIN_UTIL_SYSTEM_H +#define BITCOIN_UTIL_SYSTEM_H + +#include "attributes.h" +#include +#include "compat/assumptions.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern const char * const GRIDCOIN_CONF_FILENAME; +extern const char * const GRIDCOIN_SETTINGS_FILENAME; + +extern std::string strMiscWarning; + +void SetupEnvironment(); + +/** + * Ensure file contents are fully committed to disk, using a platform-specific + * feature analogous to fsync(). + */ +bool FileCommit(FILE *file); + +#ifdef WIN32 +fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); +#endif + +[[nodiscard]] bool RenameOver(fs::path src, fs::path dest); + +/** + * Most paths passed as configuration arguments are treated as relative to + * the datadir if they are not absolute. + * + * @param path The path to be conditionally prefixed with datadir. + * @param net_specific Forwarded to GetDataDir(). + * @return The normalized path. + */ +fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true); + +fs::path GetDefaultDataDir(); + +inline bool IsSwitchChar(char c) +{ +#ifdef WIN32 + return c == '-' || c == '/'; +#else + return c == '-'; +#endif +} + +enum class OptionsCategory { + OPTIONS, + CONNECTION, + WALLET, + WALLET_DEBUG_TEST, + ZMQ, + DEBUG_TEST, + CHAINPARAMS, + NODE_RELAY, + BLOCK_CREATION, + RPC, + GUI, + COMMANDS, + REGISTER_COMMANDS, + SCRAPER, + RESEARCHER, + + HIDDEN // Always the last option to avoid printing these in the help +}; + +struct SectionInfo +{ + std::string m_name; + std::string m_file; + int m_line; +}; + +class ArgsManager +{ +public: + enum Flags : uint32_t { + // Boolean options can accept negation syntax -noOPTION or -noOPTION=1 + ALLOW_BOOL = 0x01, + ALLOW_INT = 0x02, + ALLOW_STRING = 0x04, + ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING, + DEBUG_ONLY = 0x100, + /* Some options would cause cross-contamination if values for + * mainnet were used while running on regtest/testnet (or vice-versa). + * Setting them as NETWORK_ONLY ensures that sharing a config file + * between mainnet and regtest/testnet won't cause problems due to these + * parameters by accident. */ + NETWORK_ONLY = 0x200, + // This argument's value is sensitive (such as a password). + SENSITIVE = 0x400, + COMMAND = 0x800, + }; + +protected: + struct Arg + { + std::string m_help_param; + std::string m_help_text; + unsigned int m_flags; + }; + + mutable RecursiveMutex cs_args; + util::Settings m_settings GUARDED_BY(cs_args); + std::vector m_command GUARDED_BY(cs_args); + std::string m_network GUARDED_BY(cs_args); + std::set m_network_only_args GUARDED_BY(cs_args); + std::map> m_available_args GUARDED_BY(cs_args); + bool m_accept_any_command GUARDED_BY(cs_args){true}; + std::list m_config_sections GUARDED_BY(cs_args); + fs::path m_cached_blocks_path GUARDED_BY(cs_args); + mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args); + mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args); + + [[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false); + + /** + * Returns true if settings values from the default section should be used, + * depending on the current network and whether the setting is + * network-specific. + */ + bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args); + + /** + * Get setting value. + * + * Result will be null if setting was unset, true if "-setting" argument was passed + * false if "-nosetting" argument was passed, and a string if a "-setting=value" + * argument was passed. + */ + util::SettingsValue GetSetting(const std::string& arg) const; + + /** + * Get list of setting values. + */ + std::vector GetSettingsList(const std::string& arg) const; + +public: + ArgsManager(); + ~ArgsManager(); + + /** + * Select the network in use + */ + void SelectConfigNetwork(const std::string& network); + + [[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error); + [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); + + /** + * Log warnings for options in m_section_only_args when + * they are specified in the default section but not overridden + * on the command line or in a network-specific section in the + * config file. + */ + const std::set GetUnsuitableSectionOnlyArgs() const; + + /** + * Log warnings for unrecognized section names in the config file. + */ + const std::list GetUnrecognizedSections() const; + + struct Command { + /** The command (if one has been registered with AddCommand), or empty */ + std::string command; + /** + * If command is non-empty: Any args that followed it + * If command is empty: The unregistered command and any args that followed it + */ + std::vector args; + }; + /** + * Get the command and command args (returns std::nullopt if no command provided) + */ + std::optional GetCommand() const; + + /** + * Get blocks directory path + * + * @return Blocks path which is network specific + */ + const fs::path& GetBlocksDirPath(); + + /** + * Get data directory path + * + * @param net_specific Append network identifier to the returned path + * @return Absolute path on success, otherwise an empty path when a non-directory path would be returned + * @post Returned directory path is created unless it is empty + */ + const fs::path& GetDataDirPath(bool net_specific = true) const; + + /** + * Clear cached directory paths + */ + void ClearPathCache(); + + /** + * Return a vector of strings of the given argument + * + * @param strArg Argument to get (e.g. "-foo") + * @return command-line arguments + */ + std::vector GetArgs(const std::string& strArg) const; + + /** + * Return true if the given argument has been manually set + * + * @param strArg Argument to get (e.g. "-foo") + * @return true if the argument has been set + */ + bool IsArgSet(const std::string& strArg) const; + + /** + * Return true if the argument was originally passed as a negated option, + * i.e. -nofoo. + * + * @param strArg Argument to get (e.g. "-foo") + * @return true if the argument was passed negated + */ + bool IsArgNegated(const std::string& strArg) const; + + /** + * Return string argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param strDefault (e.g. "1") + * @return command-line argument or default value + */ + std::string GetArg(const std::string& strArg, const std::string& strDefault) const; + + /** + * Return integer argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param nDefault (e.g. 1) + * @return command-line argument (0 if invalid number) or default value + */ + int64_t GetArg(const std::string& strArg, int64_t nDefault) const; + + /** + * Return boolean argument or default value + * + * @param strArg Argument to get (e.g. "-foo") + * @param fDefault (true or false) + * @return command-line argument or default value + */ + bool GetBoolArg(const std::string& strArg, bool fDefault = false) const; + + /** + * Set an argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param strValue Value (e.g. "1") + * @return true if argument gets set, false if it already had a value + */ + bool SoftSetArg(const std::string& strArg, const std::string& strValue); + + /** + * Set a boolean argument if it doesn't already have a value + * + * @param strArg Argument to set (e.g. "-foo") + * @param fValue Value (e.g. false) + * @return true if argument gets set, false if it already had a value + */ + bool SoftSetBoolArg(const std::string& strArg, bool fValue); + + // Forces an arg setting. Called by SoftSetArg() if the arg hasn't already + // been set. Also called directly in testing. + void ForceSetArg(const std::string& strArg, const std::string& strValue); + + /** + * Returns the appropriate chain name from the program arguments. + * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. + */ + std::string GetChainName() const; + + /** + * Add argument + */ + void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat); + + /** + * Add subcommand + */ + void AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat); + + /** + * Add many hidden arguments + */ + void AddHiddenArgs(const std::vector& args); + + /** + * Clear available arguments + */ + void ClearArgs() { + LOCK(cs_args); + m_available_args.clear(); + m_network_only_args.clear(); + } + + /** + * Get the help string + */ + std::string GetHelpMessage() const; + + /** + * Return Flags for known arg. + * Return nullopt for unknown arg. + */ + std::optional GetArgFlags(const std::string& name) const; + + /** + * Read and update settings file with saved settings. This needs to be + * called after SelectParams() because the settings file location is + * network-specific. + */ + bool InitSettings(std::string& error); + + /** + * Get settings file path, or return false if read-write settings were + * disabled with -nosettings. + */ + bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false) const; + + /** + * Read settings file. Push errors to vector, or log them if null. + */ + bool ReadSettingsFile(std::vector* errors = nullptr); + + /** + * Write settings file. Push errors to vector, or log them if null. + */ + bool WriteSettingsFile(std::vector* errors = nullptr) const; + + /** + * Access settings with lock held. + */ + template + void LockSettings(Fn&& fn) + { + LOCK(cs_args); + fn(m_settings); + } + + /** + * Log the config file options and the command line arguments, + * useful for troubleshooting. + */ + void LogArgs() const; + +private: + // Helper function for LogArgs(). + void logArgsPrefix( + const std::string& prefix, + const std::string& section, + const std::map>& args) const; +}; + +extern ArgsManager gArgs; + +const fs::path &GetDataDir(bool fNetSpecific = true); + +bool CheckDataDirOption(); + +fs::path GetConfigFile(const std::string& confPath); + +fs::path GetConfigFile(); + +bool IsConfigFileEmpty(); + +/** + * @return true if help has been requested via a command-line arg + */ +bool HelpRequested(const ArgsManager& args); + +/** Add help options to the args manager */ +void SetupHelpOptions(ArgsManager& args); + +/** + * Format a string to be used as group of options in help messages + * + * @param message Group name (e.g. "RPC server options:") + * @return the formatted string + */ +std::string HelpMessageGroup(const std::string& message); + +/** + * Format a string to be used as option description in help messages + * + * @param option Option message (e.g. "-rpcuser=") + * @param message Option description (e.g. "Username for JSON-RPC connections") + * @return the formatted string + */ +std::string HelpMessageOpt(const std::string& option, const std::string& message); + +namespace util { + +//! Simplification of std insertion +template +inline void insert(Tdst& dst, const Tsrc& src) { + dst.insert(dst.begin(), src.begin(), src.end()); +} +template +inline void insert(std::set& dst, const Tsrc& src) { + dst.insert(src.begin(), src.end()); +} + +/** + * Helper function to access the contained object of a std::any instance. + * Returns a pointer to the object if passed instance has a value and the type + * matches, nullptr otherwise. + */ +template +T* AnyPtr(const std::any& any) noexcept +{ + T* const* ptr = std::any_cast(&any); + return ptr ? *ptr : nullptr; +} + +#ifdef WIN32 +class WinCmdLineArgs +{ +public: + WinCmdLineArgs(); + ~WinCmdLineArgs(); + std::pair get(); + +private: + int argc; + char** argv; + std::vector args; +}; +#endif + +} // namespace util + + + +#endif // BITCOIN_UTIL_SYSTEM_H diff --git a/src/validation.cpp b/src/validation.cpp index 808878c259..d819b442bb 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -496,12 +496,12 @@ bool GetCoinAge(const CTransaction& tx, CTxDB& txdb, uint64_t& nCoinAge) CAmount nValueIn = txPrev.vout[txin.prevout.n].nValue; bnCentSecond += CBigNum(nValueIn) * (tx.nTime - txPrev.nTime) / CENT; - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printcoinage")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printcoinage")) LogPrintf("coin age nValueIn=%" PRId64 " nTimeDiff=%d bnCentSecond=%s", nValueIn, tx.nTime - txPrev.nTime, bnCentSecond.ToString()); } CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printcoinage")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printcoinage")) LogPrintf("coin age bnCoinDay=%s", bnCoinDay.ToString()); nCoinAge = bnCoinDay.getuint64(); return true; diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index ee04bc5763..25e38852b7 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -71,10 +71,10 @@ bool CDBEnv::Open(fs::path pathEnv_) LogPrintf("dbenv.open LogDir=%s ErrorFile=%s", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; - if (GetBoolArg("-privdb", true)) + if (gArgs.GetBoolArg("-privdb", true)) nEnvFlags |= DB_PRIVATE; - int nDbCache = GetArg("-dbcache", 25); + int nDbCache = gArgs.GetArg("-dbcache", 25); dbenv.set_lg_dir(pathLogDir.string().c_str()); dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); dbenv.set_lg_bsize(1048576); @@ -308,7 +308,7 @@ void CDB::Flush() if (fReadOnly) nMinutes = 1; - bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0); + bitdb.dbenv.txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0); } void CDB::Close() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9be81c5933..fe1f3b8359 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -33,13 +33,13 @@ extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& static void accountingDeprecationCheck() { - if (!GetBoolArg("-enableaccounts", false)) + if (!gArgs.GetBoolArg("-enableaccounts", false)) throw runtime_error( "Accounting API is deprecated and will be removed in future.\n" "It can easily result in negative or odd balances if misused or misunderstood, which has happened in the field.\n" "If you still want to enable it, add to your config file enableaccounts=1\n"); - if (GetBoolArg("-staking", true)) + if (gArgs.GetBoolArg("-staking", true)) throw runtime_error("If you want to use accounting API, staking must be disabled, add to your config file staking=0\n"); } @@ -2061,7 +2061,7 @@ UniValue keypoolrefill(const UniValue& params, bool fHelp) "Fills the keypool.\n" + HelpRequiringPassphrase()); - unsigned int nSize = max(GetArg("-keypool", 100), (int64_t)0); + unsigned int nSize = max(gArgs.GetArg("-keypool", 100), (int64_t)0); if (params.size() > 0) { if (params[0].get_int() < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 936df1d14f..910d2cf398 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -591,7 +591,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, CWalletDB* pwalletdb) NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); // notify an external script when a wallet transaction comes in or is updated - std::string strCmd = GetArg("-walletnotify", ""); + std::string strCmd = gArgs.GetArg("-walletnotify", ""); if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); @@ -1612,7 +1612,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, nValueRet += vValue[i].first; } - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printpriority")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printpriority")) { //// debug print LogPrintf("SelectCoins() best subset: "); @@ -2355,7 +2355,7 @@ bool CWallet::NewKeyPool() if (IsLocked()) return false; - int64_t nKeys = max(GetArg("-keypool", 100), (int64_t)0); + int64_t nKeys = max(gArgs.GetArg("-keypool", 100), (int64_t)0); for (int i = 0; i < nKeys; i++) { int64_t nIndex = i+1; @@ -2382,7 +2382,7 @@ bool CWallet::TopUpKeyPool(unsigned int nSize) if (nSize > 0) nTargetSize = nSize; else - nTargetSize = max(GetArg("-keypool", 100), (int64_t)0); + nTargetSize = max(gArgs.GetArg("-keypool", 100), (int64_t)0); while (setKeyPool.size() < (nTargetSize + 1)) { @@ -2421,7 +2421,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) if (!HaveKey(keypool.vchPubKey.GetID())) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(keypool.vchPubKey.IsValid()); - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && GetBoolArg("-printkeypool")) + if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE) && gArgs.GetBoolArg("-printkeypool")) LogPrintf("keypool reserve %" PRId64, nIndex); } } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index eaaf7313ba..ec1ae0cd87 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -488,7 +488,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) fNoncriticalErrors = true; // ... but do warn the user there is something wrong. if (strType == "tx") // Rescan if there is a bad transaction record: - SoftSetBoolArg("-rescan", true); + gArgs.SoftSetBoolArg("-rescan", true); } } if (!strErr.empty()) @@ -632,7 +632,7 @@ void ThreadFlushWalletDB(void* parg) if (fOneThread) return; fOneThread = true; - if (!GetBoolArg("-flushwallet", true)) + if (!gArgs.GetBoolArg("-flushwallet", true)) return; unsigned int nLastSeen = nWalletDBUpdated; From 5df486f1887feaef3d2c626b4cd443fc784d6761 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Mon, 31 May 2021 12:46:44 -0400 Subject: [PATCH 159/280] Add get/updateRwSetting from Bitcoin interfaces --- src/util/system.cpp | 24 ++++++++++++++++++++++++ src/util/system.h | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/src/util/system.cpp b/src/util/system.cpp index fc572b6ed7..5e9aa84143 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1014,6 +1014,30 @@ void ArgsManager::LogArgs() const logArgsPrefix("Command-line arg:", "", m_settings.command_line_options); } +// When we port the interfaces file over from Bitcoin, these two functions should be moved there. +util::SettingsValue getRwSetting(const std::string& name) +{ + util::SettingsValue result; + gArgs.LockSettings([&](const util::Settings& settings) { + if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) { + result = *value; + } + }); + return result; +} + +bool updateRwSetting(const std::string& name, const util::SettingsValue& value) +{ + gArgs.LockSettings([&](util::Settings& settings) { + if (value.isNull()) { + settings.rw_settings.erase(name); + } else { + settings.rw_settings[name] = value; + } + }); + return gArgs.WriteSettingsFile(); +} + bool RenameOver(fs::path src, fs::path dest) { #ifdef WIN32 diff --git a/src/util/system.h b/src/util/system.h index b75b0295aa..371c721d2c 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -386,6 +386,10 @@ class ArgsManager extern ArgsManager gArgs; +// When we port the interfaces file over from Bitcoin, these two functions should be moved there. +util::SettingsValue getRwSetting(const std::string& name); +bool updateRwSetting(const std::string& name, const util::SettingsValue& value); + const fs::path &GetDataDir(bool fNetSpecific = true); bool CheckDataDirOption(); From b520e08ad683474760443c435040f3c47f684c79 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Tue, 1 Jun 2021 11:41:39 -0400 Subject: [PATCH 160/280] Implement updateRwSettings This implements an extension to the Bitcoin settings functionality to support updating multiple settings at once with only one write to the settings file. --- src/util/system.cpp | 15 +++++++++++++++ src/util/system.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/util/system.cpp b/src/util/system.cpp index 5e9aa84143..1ffed6508d 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1038,6 +1038,21 @@ bool updateRwSetting(const std::string& name, const util::SettingsValue& value) return gArgs.WriteSettingsFile(); } +bool updateRwSettings(const std::vector>& settings_in) +{ + gArgs.LockSettings([&](util::Settings& settings) { + for (const auto& iter : settings_in) + { + if (iter.second.isNull()) { + settings.rw_settings.erase(iter.first); + } else { + settings.rw_settings[iter.first] = iter.second; + } + } + }); + return gArgs.WriteSettingsFile(); +} + bool RenameOver(fs::path src, fs::path dest) { #ifdef WIN32 diff --git a/src/util/system.h b/src/util/system.h index 371c721d2c..bfd7825ced 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -390,6 +390,10 @@ extern ArgsManager gArgs; util::SettingsValue getRwSetting(const std::string& name); bool updateRwSetting(const std::string& name, const util::SettingsValue& value); +// This is to address what I think is a miss in the Bitcoin implementation, which is to efficiently update +// more than one setting at once (avoids multiple file rewrites). +bool updateRwSettings(const std::vector>& settings_in); + const fs::path &GetDataDir(bool fNetSpecific = true); bool CheckDataDirOption(); From 6e79af02f4cdc833d598f75b6ac1eab1a6ac031d Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Mon, 31 May 2021 13:58:25 -0400 Subject: [PATCH 161/280] Update research wizard ChangeMode to use new RW settings file --- src/gridcoin/researcher.cpp | 62 +++++++++++-------------------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index bb979f8182..200990181f 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -44,59 +44,27 @@ ResearcherPtr g_researcher = std::make_shared(); std::atomic g_researcher_dirty(true); //! -//! \brief Rewrite the configuration file to change investor mode and set the -//! email address directive. +//! \brief Change investor mode and set the email address directive in the +//! read-write JSON settings file //! //! \param email The email address to update the directive to. If empty, set //! the configuration to investor mode. //! -//! \return \c false if a filesystem error occurs. +//! \return \c false if an error occurs during the update. //! -bool RewriteConfigurationFileMode(const ResearcherMode mode, const std::string& email) +bool UpdateRWSettingsForMode(const ResearcherMode mode, const std::string& email) { - const fs::path config_file_path = GetConfigFile(); - std::string out; + std::vector> settings; if (mode == ResearcherMode::INVESTOR) { - out = "investor=1\n"; + settings.push_back(std::make_pair("email", util::SettingsValue(UniValue::VNULL))); + settings.push_back(std::make_pair("investor", "1")); } else if (mode == ResearcherMode::SOLO) { - out = strprintf("email=%s\n", email); + settings.push_back(std::make_pair("email", util::SettingsValue(email))); + settings.push_back(std::make_pair("investor", "0")); } - try { - fsbridge::ifstream config_file_in(config_file_path); - std::string line; - - LOCK(cs_main); - - while (std::getline(config_file_in, line)) { - if (!boost::starts_with(line, "email=") - && !boost::starts_with(line, "investor=")) - { - out += line; - out += "\n"; - } - } - - config_file_in.close(); - } catch (const std::exception& e) { - error("%s: Failed to read config file: %s", __func__, e.what()); - return false; - } - - try { - fsbridge::ofstream config_file_out(config_file_path); - - LOCK(cs_main); - - config_file_out << out; - config_file_out.close(); - } catch (const std::exception& e) { - error("%s: Failed to write config file: %s", __func__, e.what()); - return false; - } - - return true; + return ::updateRwSettings(settings); } //! @@ -1141,7 +1109,13 @@ void Researcher::RunRenewBeaconJob() std::string Researcher::Email() { - std::string email = gArgs.GetArg("-email", ""); + std::string email; + + // If the investor mode flag is set, it should override the email setting. This is especially important now + // that the read-write settings file is populated, which overrides the settings in the config file. + if (gArgs.GetBoolArg("-investor", false)) return email; + + email = gArgs.GetArg("-email", ""); boost::to_lower(email); return email; @@ -1397,7 +1371,7 @@ bool Researcher::ChangeMode(const ResearcherMode mode, std::string email) return true; } - if (!RewriteConfigurationFileMode(mode, email)) { + if (!UpdateRWSettingsForMode(mode, email)) { return false; } From 45b8ecdc131f9b47354e93d1034151d76eceae05 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Wed, 2 Jun 2021 11:25:45 -0400 Subject: [PATCH 162/280] Implement staking options category and move staking options there --- src/init.cpp | 18 +++++++++++------- src/util/system.cpp | 3 +++ src/util/system.h | 1 + 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 3a078484a1..03d6e10ab2 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -318,7 +318,6 @@ void SetupServerArgs() ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-blockminsize=", "Set minimum block size in bytes (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - // TODO: Check the maximize block size default. argsman.AddArg("-blockmaxsize=", strprintf("Set maximum block size in bytes (default: %u)", MAX_BLOCK_SIZE_GEN/2), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-blockprioritysize=", "Set maximum size of high-priority/low-fee transactions in bytes" @@ -352,25 +351,29 @@ void SetupServerArgs() " prefixed by datadir location. (default: %s)", GRIDCOIN_CONF_FILENAME, GRIDCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + //TODO: Implement startupnotify option //argsman.AddArg("-startupnotify=", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + + + // Staking argsman.AddArg("-enablesidestaking", "Enable side staking functionality (default: 0)", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); argsman.AddArg("-staking", "Allow wallet to stake if conditions to stake are met (default: 1)", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); argsman.AddArg("-sidestake=", "Sidestake destination and allocation entry. There can be as many " "specified as desired. Only six per stake can be sent. If more than " "six are specified. Six are randomly chosen for each stake. Only active " "if -enablesidestaking is set.", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); argsman.AddArg("-enablestakesplit", "Enable unspent output spitting when staking to optimize staking efficiency " "(default: 0", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); argsman.AddArg("-stakingefficiency=", "Specify target staking efficiency for stake splitting (default: 90, " "clamped to [75, 98])", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); argsman.AddArg("-minstakesplitvalue=", strprintf("Specify minimum output value for post split output when stake " "splitting (default: %" PRId64 "GRC)", MIN_STAKE_SPLIT_VALUE_GRC), - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); // Scraper argsman.AddArg("-scraper", "Activate scraper for statistics downloads. This will only work if the node has a wallet " @@ -520,6 +523,7 @@ void SetupServerArgs() "Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + // Additional hidden options hidden_args.emplace_back("-devbuild"); hidden_args.emplace_back("-scrapersleep"); hidden_args.emplace_back("-activebeforesb"); diff --git a/src/util/system.cpp b/src/util/system.cpp index 1ffed6508d..bbacfb38f0 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -611,6 +611,9 @@ std::string ArgsManager::GetHelpMessage() const case OptionsCategory::REGISTER_COMMANDS: usage += HelpMessageGroup("Register Commands:"); break; + case OptionsCategory::STAKING: + usage += HelpMessageGroup("Staking options"); + break; case OptionsCategory::SCRAPER: usage += HelpMessageGroup("Scraper options:"); break; diff --git a/src/util/system.h b/src/util/system.h index bfd7825ced..7671c84649 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -85,6 +85,7 @@ enum class OptionsCategory { GUI, COMMANDS, REGISTER_COMMANDS, + STAKING, SCRAPER, RESEARCHER, From 5ab166ef19531b846527fc6a519667094d288393 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Tue, 8 Jun 2021 05:47:42 -0500 Subject: [PATCH 163/280] Fix "master" branch build status badge in readme The build status badge for the "master" branch pointed to the status for the "staging" branch. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd64e58009..16727ef2bf 100644 --- a/README.md +++ b/README.md @@ -85,4 +85,4 @@ Build Status | Development | Staging | Master | |----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| -| ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=development) | ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=staging) | ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=staging) | +| ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=development) | ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=staging) | ![Build Status](https://github.com/gridcoin-community/Gridcoin-Research/actions/workflows/ci.yml/badge.svg?branch=master) | From ca6a72aa130a35bdd7d2d188a92be6ed4d4ee84c Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:34 -0500 Subject: [PATCH 164/280] Expand poll registry API to filter by finished polls only The PollRegistry class provides a sequence iterator that could perform rudimentary filtering to exclude finished polls, but it could not walk the sequence for finished polls only. This updates the polls filtering mechanism to support that use case with a structure that enables other types of filters for the future. --- src/Makefile.am | 1 + src/gridcoin/voting/filter.h | 17 +++++++++ src/gridcoin/voting/registry.cpp | 60 +++++++++++++++++++++++--------- src/gridcoin/voting/registry.h | 30 ++++++++++++---- 4 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 src/gridcoin/voting/filter.h diff --git a/src/Makefile.am b/src/Makefile.am index 1a2ce76acc..e6d2635082 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -123,6 +123,7 @@ GRIDCOIN_CORE_H = \ gridcoin/upgrade.h \ gridcoin/voting/builders.h \ gridcoin/voting/claims.h \ + gridcoin/voting/filter.h \ gridcoin/voting/fwd.h \ gridcoin/voting/payloads.h \ gridcoin/voting/poll.h \ diff --git a/src/gridcoin/voting/filter.h b/src/gridcoin/voting/filter.h new file mode 100644 index 0000000000..e9a4b2a085 --- /dev/null +++ b/src/gridcoin/voting/filter.h @@ -0,0 +1,17 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace GRC { +//! +//! \brief Bit flags that represents attributes to filter polls by. +//! +enum PollFilterFlag +{ + NO_FILTER = 0, //!< No active filter. Include all results. + ACTIVE = 1, //!< Include unfinished polls. + FINISHED = 2, //!< Include finished polls. +}; +} // namespace GRC diff --git a/src/gridcoin/voting/registry.cpp b/src/gridcoin/voting/registry.cpp index 4eb5ff24b6..0e6dc42d06 100644 --- a/src/gridcoin/voting/registry.cpp +++ b/src/gridcoin/voting/registry.cpp @@ -539,31 +539,36 @@ void PollRegistry::DeleteVote(const ContractContext& ctx) using Sequence = PollRegistry::Sequence; -Sequence::Sequence(const PollMapByTitle& polls, const bool active_only) - : m_polls(polls), m_active_only(active_only) +Sequence::Sequence(const PollMapByTitle& polls, const FilterFlag flags) + : m_polls(polls), m_flags(flags) { } +Sequence Sequence::Where(const FilterFlag flags) const +{ + return Sequence(m_polls, flags); +} + Sequence Sequence::OnlyActive(const bool active_only) const { - return Sequence(m_polls, active_only); + int flags = m_flags; + + if (active_only) { + flags = (flags & ~FINISHED) | ACTIVE; + } + + return Sequence(m_polls, static_cast(flags)); } Sequence::Iterator Sequence::begin() const { - auto iter = m_polls.begin(); - auto end = m_polls.end(); int64_t now = 0; - if (m_active_only) { + if (!((m_flags & ACTIVE) && (m_flags & FINISHED))) { now = GetAdjustedTime(); - - while (iter != end && iter->second.Expired(now)) { - ++iter; - } } - return Iterator(iter, end, m_active_only, now); + return Iterator(m_polls.begin(), m_polls.end(), m_flags, now); } Sequence::Iterator Sequence::end() const @@ -580,13 +585,14 @@ using Iterator = PollRegistry::Sequence::Iterator; Iterator::Iterator( BaseIterator iter, BaseIterator end, - const bool active_only, + const FilterFlag flags, const int64_t now) : m_iter(iter) , m_end(end) - , m_active_only(active_only) + , m_flags(flags) , m_now(now) { + SeekNextMatch(); } Iterator::Iterator(BaseIterator end) : m_iter(end), m_end(end) @@ -615,9 +621,8 @@ Iterator::pointer Iterator::operator->() const Iterator& Iterator::operator++() { - do { - ++m_iter; - } while (m_active_only && m_iter != m_end && m_iter->second.Expired(m_now)); + ++m_iter; + SeekNextMatch(); return *this; } @@ -639,3 +644,26 @@ bool Iterator::operator!=(const Iterator& other) const { return m_iter != other.m_iter; } + +void Iterator::SeekNextMatch() +{ + if (m_flags == FilterFlag::NO_FILTER) { + return; + } + + while (m_iter != m_end) { + if (m_now > 0) { + if (m_flags & ACTIVE) { + if (!m_iter->second.Expired(m_now)) { + break; + } + } else { + if (m_iter->second.Expired(m_now)) { + break; + } + } + } + + ++m_iter; + } +} diff --git a/src/gridcoin/voting/registry.h b/src/gridcoin/voting/registry.h index 36f2889b0f..3a2d712f8a 100644 --- a/src/gridcoin/voting/registry.h +++ b/src/gridcoin/voting/registry.h @@ -5,6 +5,7 @@ #pragma once #include "gridcoin/contract/handler.h" +#include "gridcoin/voting/filter.h" #include "gridcoin/voting/fwd.h" class CTxDB; @@ -132,6 +133,8 @@ class PollRegistry : public IContractHandler class Sequence { public: + using FilterFlag = PollFilterFlag; + //! //! \brief Behaves like a forward \c const iterator. //! @@ -152,7 +155,7 @@ class PollRegistry : public IContractHandler Iterator( BaseIterator iter, BaseIterator end, - const bool active_only, + const FilterFlag flags, const int64_t now); //! @@ -209,23 +212,38 @@ class PollRegistry : public IContractHandler private: BaseIterator m_iter; //!< The current position. BaseIterator m_end; //!< Element after the end of the sequence. - bool m_active_only; //!< Whether to skip finished polls. + FilterFlag m_flags; //!< Attributes to filter polls by. int64_t m_now; //!< Current time in seconds. + + //! + //! \brief Advance the iterator to the next item that matches the + //! configured filters. + //! + void SeekNextMatch(); }; // Iterator //! //! \brief Initialize a poll sequence. //! - //! \param polls The set of poll references in the registry. - //! \param active_only Whether the sequence skips finished polls. + //! \param polls The set of poll references in the registry. + //! \param flags Attributes to filter polls by. //! - Sequence(const PollMapByTitle& polls, const bool active_only = false); + Sequence(const PollMapByTitle& polls, const FilterFlag flags = NO_FILTER); + + //! + //! \brief Set the attributes to filter polls by. + //! + //! \return A new sequence for the specified poll filters. + //! + Sequence Where(const FilterFlag flags) const; //! //! \brief Set whether the sequence skips finished polls. //! //! \param active_only Whether the sequence skips finished polls. //! + //! \return A new sequence for the specified poll status filters. + //! Sequence OnlyActive(const bool active_only = true) const; //! @@ -240,7 +258,7 @@ class PollRegistry : public IContractHandler private: const PollMapByTitle& m_polls; //!< Poll references in the registry. - bool m_active_only; //!< Whether to skip finished polls. + FilterFlag m_flags; //!< Attributes to filter polls by. }; // Sequence //! From 47932f29439771dc23f81cb033f36d9da45e3729 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:36 -0500 Subject: [PATCH 165/280] Create voting GUI view model core abstraction layer This adds view models that provide access to core voting functionality. --- gridcoinresearch.pro | 4 + src/Makefile.qt.include | 8 +- src/gridcoin/voting/result.cpp | 1 + src/gridcoin/voting/result.h | 1 + src/qt/voting/polltablemodel.cpp | 237 ++++++++++++++++++++++++ src/qt/voting/polltablemodel.h | 61 +++++++ src/qt/voting/votingmodel.cpp | 297 +++++++++++++++++++++++++++++++ src/qt/voting/votingmodel.h | 123 +++++++++++++ 8 files changed, 731 insertions(+), 1 deletion(-) create mode 100644 src/qt/voting/polltablemodel.cpp create mode 100644 src/qt/voting/polltablemodel.h create mode 100644 src/qt/voting/votingmodel.cpp create mode 100644 src/qt/voting/votingmodel.h diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 49aec3b393..3e3a43da63 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -185,6 +185,8 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/researcher/researcherwizardpoolsummarypage.h \ src/qt/researcher/researcherwizardprojectspage.h \ src/qt/researcher/researcherwizardsummarypage.h \ + src/qt/voting/polltablemodel.h \ + src/qt/voting/votingmodel.h \ src/qt/transactiontablemodel.h \ src/qt/addresstablemodel.h \ src/qt/optionsdialog.h \ @@ -292,6 +294,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/researcher/researcherwizardpoolsummarypage.cpp \ src/qt/researcher/researcherwizardprojectspage.cpp \ src/qt/researcher/researcherwizardsummarypage.cpp \ + src/qt/voting/polltablemodel.cpp \ + src/qt/voting/votingmodel.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ src/qt/optionsdialog.cpp \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 93ae56198d..5319ba3a83 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -166,7 +166,9 @@ QT_MOC_CPP = \ qt/researcher/moc_researcherwizardpoolpage.cpp \ qt/researcher/moc_researcherwizardpoolsummarypage.cpp \ qt/researcher/moc_researcherwizardprojectspage.cpp \ - qt/researcher/moc_researcherwizardsummarypage.cpp + qt/researcher/moc_researcherwizardsummarypage.cpp \ + qt/voting/moc_polltablemodel.cpp \ + qt/voting/moc_votingmodel.cpp GRIDCOIN_MM = \ qt/macdockiconhandler.mm \ @@ -250,6 +252,8 @@ GRIDCOINRESEARCH_QT_H = \ qt/transactionview.h \ qt/upgradeqt.h \ qt/votingdialog.h \ + qt/voting/polltablemodel.h \ + qt/voting/votingmodel.h \ qt/walletmodel.h \ qt/winshutdownmonitor.h @@ -316,6 +320,8 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/transactionview.cpp \ qt/upgradeqt.cpp \ qt/votingdialog.cpp \ + qt/voting/polltablemodel.cpp \ + qt/voting/votingmodel.cpp \ qt/walletmodel.cpp \ qt/winshutdownmonitor.cpp diff --git a/src/gridcoin/voting/result.cpp b/src/gridcoin/voting/result.cpp index 4582d1c128..e984fe4703 100644 --- a/src/gridcoin/voting/result.cpp +++ b/src/gridcoin/voting/result.cpp @@ -1069,6 +1069,7 @@ PollResult::PollResult(Poll poll) : m_poll(std::move(poll)) , m_total_weight(0) , m_invalid_votes(0) + , m_finished(poll.Expired(GetAdjustedTime())) { m_responses.resize(m_poll.Choices().size()); } diff --git a/src/gridcoin/voting/result.h b/src/gridcoin/voting/result.h index b99bf89aed..7a4c455298 100644 --- a/src/gridcoin/voting/result.h +++ b/src/gridcoin/voting/result.h @@ -79,6 +79,7 @@ class PollResult const Poll m_poll; //!< The poll associated with the result. Weight m_total_weight; //!< Aggregate weight of all the votes submitted. size_t m_invalid_votes; //!< Number of votes that failed validation. + bool m_finished; //!< Whether the poll finished as of this result. //! //! \brief The aggregated voting weight tallied for each poll choice. diff --git a/src/qt/voting/polltablemodel.cpp b/src/qt/voting/polltablemodel.cpp new file mode 100644 index 0000000000..b92cab7209 --- /dev/null +++ b/src/qt/voting/polltablemodel.cpp @@ -0,0 +1,237 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/guiutil.h" +#include "qt/voting/polltablemodel.h" +#include "qt/voting/votingmodel.h" + +#include +#include +#include + +using namespace GRC; + +namespace { +class PollTableDataModel : public QAbstractTableModel +{ +public: + PollTableDataModel() + { + qRegisterMetaType>(); + qRegisterMetaType(); + + m_columns + << tr("Title") + << tr("Expiration") + << tr("Weight Type") + << tr("Votes") + << tr("Total Weight") + << tr("Top Answer"); + } + + int rowCount(const QModelIndex &parent) const override + { + Q_UNUSED(parent); + return m_rows.size(); + } + + int columnCount(const QModelIndex &parent) const override + { + Q_UNUSED(parent); + return m_columns.size(); + } + + QVariant data(const QModelIndex &index, int role) const override + { + if (!index.isValid()) { + return QVariant(); + } + + const PollItem* row = static_cast(index.internalPointer()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case PollTableModel::Title: + return row->m_title; + case PollTableModel::Expiration: + return GUIUtil::dateTimeStr(row->m_expiration); + case PollTableModel::WeightType: + return row->m_weight_type; + case PollTableModel::TotalVotes: + return row->m_total_votes; + case PollTableModel::TotalWeight: + return QString::number(row->m_total_weight); + case PollTableModel::TopAnswer: + return row->m_top_answer; + } + break; + + case Qt::TextAlignmentRole: + switch (index.column()) { + case PollTableModel::TotalVotes: + // Pass-through case + case PollTableModel::TotalWeight: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + } + break; + + case PollTableModel::SortRole: + switch (index.column()) { + case PollTableModel::Title: + return row->m_title; + case PollTableModel::Expiration: + return row->m_expiration; + case PollTableModel::WeightType: + return row->m_weight_type; + case PollTableModel::TotalVotes: + return row->m_total_votes; + case PollTableModel::TotalWeight: + return QVariant::fromValue(row->m_total_weight); + case PollTableModel::TopAnswer: + return row->m_top_answer; + } + break; + } + + return QVariant(); + } + + QVariant headerData(int section, Qt::Orientation orientation, int role) const override + { + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole && section < m_columns.size()) { + return m_columns[section]; + } + } + + return QVariant(); + } + + QModelIndex index(int row, int column, const QModelIndex &parent) const override + { + Q_UNUSED(parent); + + if (row > static_cast(m_rows.size())) { + return QModelIndex(); + } + + void* data = static_cast(const_cast(&m_rows[row])); + + return createIndex(row, column, data); + } + + Qt::ItemFlags flags(const QModelIndex &index) const override + { + if (!index.isValid()) { + return Qt::NoItemFlags; + } + + return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); + } + + void reload(std::vector rows) + { + emit layoutAboutToBeChanged(); + m_rows = std::move(rows); + emit layoutChanged(); + } + +private: + QStringList m_columns; + std::vector m_rows; +}; // PollTableDataModel +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: PollTableModel +// ----------------------------------------------------------------------------- + +PollTableModel::PollTableModel(QObject* parent) + : QSortFilterProxyModel(parent) + , m_data_model(new PollTableDataModel()) + , m_filter_flags(GRC::PollFilterFlag::NO_FILTER) +{ + setSourceModel(m_data_model.get()); + setDynamicSortFilter(true); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setFilterKeyColumn(ColumnIndex::Title); + setSortCaseSensitivity(Qt::CaseInsensitive); + setSortRole(SortRole); +} + +PollTableModel::~PollTableModel() +{ + // Nothing to do yet... +} + +void PollTableModel::setModel(VotingModel* model) +{ + m_model = model; +} + +void PollTableModel::setPollFilterFlags(PollFilterFlag flags) +{ + m_filter_flags = flags; +} + +bool PollTableModel::includesActivePolls() const +{ + return (m_filter_flags & PollFilterFlag::ACTIVE) != 0; +} + +int PollTableModel::size() const +{ + return m_data_model->rowCount(QModelIndex()); +} + +bool PollTableModel::empty() const +{ + return size() == 0; +} + +QString PollTableModel::columnName(int offset) const +{ + return m_data_model->headerData(offset, Qt::Horizontal, Qt::DisplayRole).toString(); +} + +const PollItem* PollTableModel::rowItem(int row) const +{ + QModelIndex index = this->index(row, 0, QModelIndex()); + index = mapToSource(index); + + return static_cast(index.internalPointer()); +} + +void PollTableModel::refresh() +{ + if (!m_model || !m_refresh_mutex.tryLock()) { + return; + } + + QtConcurrent::run([this]() { + static_cast(m_data_model.get()) + ->reload(m_model->buildPollTable(m_filter_flags)); + + m_refresh_mutex.unlock(); + }); +} + +void PollTableModel::changeTitleFilter(const QString& pattern) +{ + emit layoutAboutToBeChanged(); + setFilterFixedString(pattern); + emit layoutChanged(); +} + +Qt::SortOrder PollTableModel::sort(int column) +{ + if (sortColumn() == column) { + QSortFilterProxyModel::sort(column, static_cast(!sortOrder())); + } else { + QSortFilterProxyModel::sort(column, Qt::AscendingOrder); + } + + return sortOrder(); +} diff --git a/src/qt/voting/polltablemodel.h b/src/qt/voting/polltablemodel.h new file mode 100644 index 0000000000..94260b413c --- /dev/null +++ b/src/qt/voting/polltablemodel.h @@ -0,0 +1,61 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLTABLEMODEL_H +#define VOTING_POLLTABLEMODEL_H + +#include "gridcoin/voting/filter.h" + +#include +#include +#include + +class PollItem; +class VotingModel; + +class PollTableModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + enum ColumnIndex + { + Title, + Expiration, + WeightType, + TotalVotes, + TotalWeight, + TopAnswer, + }; + + enum Roles + { + SortRole = Qt::UserRole, + }; + + explicit PollTableModel(QObject* parent = nullptr); + ~PollTableModel(); + + void setModel(VotingModel* model = nullptr); + void setPollFilterFlags(GRC::PollFilterFlag flags); + bool includesActivePolls() const; + + int size() const; + bool empty() const; + QString columnName(int offset) const; + const PollItem* rowItem(int row) const; + +public slots: + void refresh(); + void changeTitleFilter(const QString& pattern); + Qt::SortOrder sort(int column); + +private: + VotingModel* m_model; + std::unique_ptr m_data_model; + GRC::PollFilterFlag m_filter_flags; + QMutex m_refresh_mutex; +}; + +#endif // VOTING_POLLTABLEMODEL_H diff --git a/src/qt/voting/votingmodel.cpp b/src/qt/voting/votingmodel.cpp new file mode 100644 index 0000000000..dbc83d2764 --- /dev/null +++ b/src/qt/voting/votingmodel.cpp @@ -0,0 +1,297 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "hash.h" +#include "gridcoin/contract/contract.h" +#include "gridcoin/project.h" +#include "gridcoin/voting/builders.h" +#include "gridcoin/voting/poll.h" +#include "gridcoin/voting/registry.h" +#include "gridcoin/voting/result.h" +#include "qt/voting/votingmodel.h" +#include "qt/walletmodel.h" +#include "sync.h" + +using namespace GRC; + +extern CCriticalSection cs_main; + +namespace { +std::optional BuildPollItem(const PollRegistry::Sequence::Iterator& iter) +{ + const PollResultOption result = PollResult::BuildFor(iter->Ref()); + + if (!result) { + return std::nullopt; + } + + const Poll& poll = result->m_poll; + + PollItem item; + item.m_id = QString::fromStdString(iter->Ref().Txid().ToString()); + item.m_title = QString::fromStdString(poll.m_title).replace("_", " "); + item.m_question = QString::fromStdString(poll.m_question).replace("_", " "); + item.m_url = QString::fromStdString(poll.m_url).trimmed(); + item.m_start_time = QDateTime::fromMSecsSinceEpoch(poll.m_timestamp * 1000); + item.m_expiration = QDateTime::fromMSecsSinceEpoch(poll.Expiration() * 1000); + item.m_weight_type = QString::fromStdString(poll.WeightTypeToString()); + item.m_response_type = QString::fromStdString(poll.ResponseTypeToString()); + item.m_total_votes = result->m_votes.size(); + item.m_total_weight = result->m_total_weight / COIN; + item.m_finished = result->m_finished; + item.m_multiple_choice = poll.AllowsMultipleChoices(); + + if (!item.m_url.startsWith("http://") && !item.m_url.startsWith("https://")) { + item.m_url.prepend("http://"); + } + + for (size_t i = 0; i < result->m_responses.size(); ++i) { + item.m_choices.emplace_back( + QString::fromStdString(poll.Choices().At(i)->m_label), + result->m_responses[i].m_votes, + result->m_responses[i].m_weight / COIN); + } + + if (!result->m_votes.empty()) { + item.m_top_answer = QString::fromStdString(result->WinnerLabel()).replace("_", " "); + } + + return item; +} +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: VotingModel +// ----------------------------------------------------------------------------- + +VotingModel::VotingModel(WalletModel& wallet_model) + : m_registry(GetPollRegistry()) + , m_wallet_model(wallet_model) +{ +} + +VotingModel::~VotingModel() +{ +} + +int VotingModel::minPollDurationDays() +{ + return Poll::MIN_DURATION_DAYS; +} + +int VotingModel::maxPollDurationDays() +{ + // The protocol allows poll durations up to 180 days. To limit unhelpful + // or unintentional poll durations, user-facing pieces discourage a poll + // longer than: + // + return 90; // days +} + +int VotingModel::maxPollTitleLength() +{ + // Not strictly accurate: the protocol limits the max length in bytes, but + // Qt limits field lengths in UTF-8 characters which may be represented by + // more than one byte. + // + return Poll::MAX_TITLE_SIZE; +} + +int VotingModel::maxPollUrlLength() +{ + // Not strictly accurate: the protocol limits the max length in bytes, but + // Qt limits field lengths in UTF-8 characters which may be represented by + // more than one byte. + // + return Poll::MAX_URL_SIZE; +} + +int VotingModel::maxPollQuestionLength() +{ + // Not strictly accurate: the protocol limits the max length in bytes, but + // Qt limits field lengths in UTF-8 characters which may be represented by + // more than one byte. + // + return Poll::MAX_QUESTION_SIZE; +} + +int VotingModel::maxPollChoiceLabelLength() +{ + // Not strictly accurate: the protocol limits the max length in bytes, but + // Qt limits field lengths in UTF-8 characters which may be represented by + // more than one byte. + // + return Poll::Choice::MAX_LABEL_SIZE; +} + +QStringList VotingModel::getActiveProjectNames() const +{ + QStringList names; + + for (const auto& project : GetWhitelist().Snapshot().Sorted()) { + names << QString::fromStdString(project.m_name); + } + + return names; +} + +std::vector VotingModel::buildPollTable(const PollFilterFlag flags) const +{ + std::vector items; + + LOCK(cs_main); + + for (const auto iter : m_registry.Polls().Where(flags)) { + if (std::optional item = BuildPollItem(iter)) { + items.push_back(std::move(*item)); + } + } + + return items; +} + +CAmount VotingModel::estimatePollFee() const +{ + // TODO: add core API for more precise fee estimation. + return 50 * COIN; +} + +VotingResult VotingModel::sendPoll( + const QString& title, + const int duration_days, + const QString& question, + const QString& url, + const int weight_type, + const int response_type, + const QStringList& choices) const +{ + PollBuilder builder = PollBuilder(); + + try { + builder = builder + .SetType(PollType::SURVEY) + .SetTitle(title.toStdString()) + .SetDuration(duration_days) + .SetQuestion(question.toStdString()) + .SetWeightType(weight_type) + .SetResponseType(response_type) + .SetUrl(url.toStdString()); + + for (const auto& choice : choices) { + builder = builder.AddChoice(choice.toStdString()); + } + } catch (const VotingError& e) { + return VotingResult(QString::fromStdString(e.what())); + } + + const WalletModel::UnlockContext unlock_context(m_wallet_model.requestUnlock()); + + if (!unlock_context.isValid()) { + return VotingResult(tr("Please unlock the wallet.")); + } + + uint256 txid; + + try { + txid = SendPollContract(std::move(builder)); + } catch (const VotingError& e) { + return VotingResult(QString::fromStdString(e.what())); + } + + return VotingResult(txid); +} + +VotingResult VotingModel::sendVote( + const QString& poll_id, + const std::vector& choice_offsets) const +{ + LOCK(cs_main); + + const uint256 poll_txid = uint256S(poll_id.toStdString()); + const PollReference* ref = m_registry.TryByTxid(poll_txid); + + if (!ref) { + return VotingResult(tr("Poll not found.")); + } + + const PollOption poll = ref->TryReadFromDisk(); + + if (!poll) { + return VotingResult(tr("Failed to load poll from disk")); + } + + try { + VoteBuilder builder = VoteBuilder::ForPoll(*poll, ref->Txid()); + builder = builder.AddResponses(choice_offsets); + + const WalletModel::UnlockContext unlock_context(m_wallet_model.requestUnlock()); + + if (!unlock_context.isValid()) { + return VotingResult(tr("Please unlock the wallet.")); + } + + const uint256 txid = SendVoteContract(std::move(builder)); + + return VotingResult(txid); + } catch (const VotingError& e){ + return VotingResult(e.what()); + } +} + +// ----------------------------------------------------------------------------- +// Class: VoteResultItem +// ----------------------------------------------------------------------------- + +VoteResultItem::VoteResultItem(QString label, double votes, uint64_t weight) + : m_label(label) + , m_votes(votes) + , m_weight(weight) +{ +} + +bool VoteResultItem::operator<(const VoteResultItem& other) const +{ + return m_weight < other.m_weight; +} + +// ----------------------------------------------------------------------------- +// Class: VotingResult +// ----------------------------------------------------------------------------- + +VotingResult::VotingResult(const uint256& txid) + : m_value(QString::fromStdString(txid.ToString())) + , m_ok(true) +{ +} + +VotingResult::VotingResult(const QString& error) + : m_value(error) + , m_ok(false) +{ +} + +bool VotingResult::ok() const +{ + return m_ok; +} + +QString VotingResult::txid() const +{ + if (!m_ok) { + return QString::fromStdString(uint256().ToString()); + } + + return m_value; +} + +QString VotingResult::error() const +{ + if (m_ok) { + return QString(); + } + + return m_value; +} diff --git a/src/qt/voting/votingmodel.h b/src/qt/voting/votingmodel.h new file mode 100644 index 0000000000..dfa6263bf0 --- /dev/null +++ b/src/qt/voting/votingmodel.h @@ -0,0 +1,123 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_VOTINGMODEL_H +#define VOTING_VOTINGMODEL_H + +#include "amount.h" +#include "gridcoin/voting/filter.h" +#include "qt/voting/poll_types.h" + +#include +#include +#include + +namespace GRC { +class Poll; +class PollRegistry; +} + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +class uint256; +class WalletModel; + +//! +//! \brief An aggregate result for one choice of a poll. +//! +class VoteResultItem +{ +public: + QString m_label; + double m_votes; + uint64_t m_weight; + + explicit VoteResultItem(QString label, double votes, uint64_t weight); + bool operator<(const VoteResultItem& other) const; +}; + +//! +//! \brief Represents a poll contract and associated responses. +//! +class PollItem +{ +public: + QString m_id; + QString m_title; + QString m_question; + QString m_url; + QDateTime m_start_time; + QDateTime m_expiration; + QString m_weight_type; + QString m_response_type; + QString m_top_answer; + uint32_t m_total_votes; + uint64_t m_total_weight; + bool m_finished; + bool m_multiple_choice; + std::vector m_choices; +}; + +//! +//! \brief A variant-like object that stores the result of an attempt to create +//! a poll or vote contract transaction. +//! +class VotingResult +{ +public: + explicit VotingResult(const uint256& txid); + explicit VotingResult(const QString& error); + + bool ok() const; + QString error() const; + QString txid() const; + +private: + QString m_value; + bool m_ok; +}; + +//! +//! \brief Presents voting information for UI components. +//! +class VotingModel : public QObject +{ + Q_OBJECT + +public: + VotingModel(WalletModel& wallet_model); + ~VotingModel(); + + static int minPollDurationDays(); + static int maxPollDurationDays(); + static int maxPollTitleLength(); + static int maxPollUrlLength(); + static int maxPollQuestionLength(); + static int maxPollChoiceLabelLength(); + + QStringList getActiveProjectNames() const; + std::vector buildPollTable(const GRC::PollFilterFlag flags) const; + + CAmount estimatePollFee() const; + + VotingResult sendPoll( + const QString& title, + const int duration_days, + const QString& question, + const QString& url, + const int weight_type, + const int response_type, + const QStringList& choices) const; + VotingResult sendVote( + const QString& poll_id, + const std::vector& choice_offsets) const; + +private: + GRC::PollRegistry& m_registry; + WalletModel& m_wallet_model; +}; // VotingModel + +#endif // VOTING_VOTINGMODEL_H From 00941d879c9f0ff2899d8bdbd031807a6966f731 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:39 -0500 Subject: [PATCH 166/280] Install GUIUtil::formatNiceTimeOffset() from Bitcoin This adds a function that formats a human-friendly time span from newer Bitcoin code modified to round the result time span half-up instead of flooring it. --- src/qt/guiutil.cpp | 46 +++++++++++++++++++++++++++++++ src/qt/guiutil.h | 2 ++ src/qt/locale/bitcoin_en.ts | 54 +++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 6e8d5e779b..4f9f9c176e 100755 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -60,6 +60,52 @@ QString formatTimeOffset(int64_t nTimeOffset) return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); } +QString formatNiceTimeOffset(qint64 secs) +{ + // Represent time from last generated block in human readable text + QString timeBehindText; + const int HOUR_IN_SECONDS = 60*60; + const int DAY_IN_SECONDS = 24*60*60; + const int WEEK_IN_SECONDS = 7*24*60*60; + const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar + + constexpr auto round_half_up = [](int secs, int timeframe_secs) + { + return (secs + (timeframe_secs / 2)) / timeframe_secs; + }; + + if(secs < 60) + { + timeBehindText = QObject::tr("%n second(s)", "", secs); + } + else if(secs < 2*HOUR_IN_SECONDS) + { + timeBehindText = QObject::tr("%n minute(s)", "", round_half_up(secs, 60)); + } + else if(secs < 2*DAY_IN_SECONDS) + { + timeBehindText = QObject::tr("%n hour(s)", "", round_half_up(secs, HOUR_IN_SECONDS)); + } + else if(secs < 2*WEEK_IN_SECONDS) + { + timeBehindText = QObject::tr("%n day(s)", "", round_half_up(secs, DAY_IN_SECONDS)); + } + else if(secs < YEAR_IN_SECONDS) + { + timeBehindText = QObject::tr("%n week(s)", "", round_half_up(secs, WEEK_IN_SECONDS)); + } + else + { + qint64 years = secs / YEAR_IN_SECONDS; + qint64 remainder = secs % YEAR_IN_SECONDS; + timeBehindText = QObject::tr("%1 and %2") + .arg(QObject::tr("%n year(s)", "", years)) + .arg(QObject::tr("%n week(s)","", round_half_up(remainder, WEEK_IN_SECONDS))); + } + + return timeBehindText; +} + QString formatBytes(uint64_t bytes) { if(bytes < 1024) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index c6f2cc8265..25b2520c53 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -30,6 +30,8 @@ namespace GUIUtil // Format Node Time Offset QString formatTimeOffset(int64_t nTimeOffset); + QString formatNiceTimeOffset(qint64 secs); + // Format Bytes QString formatBytes(uint64_t bytes); diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 36dcc0e738..890b9e1d37 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -1981,6 +1981,60 @@ This label turns red, if the priority is smaller than "medium". %1 s + + + %n second(s) + + %n second + %n seconds + + + + + %n minute(s) + + %n minute + %n minutes + + + + + %n hour(s) + + %n hour + %n hours + + + + + %n day(s) + + %n day + %n days + + + + + + %n week(s) + + %n week + %n weeks + + + + + %1 and %2 + + + + + %n year(s) + + %n year + %n years + + %1 B From cc85f4c9393c124e9b761d04bac4bee8f9eaf863 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:42 -0500 Subject: [PATCH 167/280] Add core signal for new poll received events This adds a core signal that the poll registry broadcasts when a node receives a poll contract in a transaction so that the GUI can respond to the event. --- src/gridcoin/voting/registry.cpp | 11 +++++++ src/gridcoin/voting/registry.h | 7 +++++ src/qt/bitcoin.cpp | 10 ++++++ src/qt/voting/votingmodel.cpp | 52 +++++++++++++++++++++++++++++++- src/qt/voting/votingmodel.h | 14 ++++++++- src/ui_interface.h | 3 ++ 6 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/gridcoin/voting/registry.cpp b/src/gridcoin/voting/registry.cpp index 0e6dc42d06..345fd9b189 100644 --- a/src/gridcoin/voting/registry.cpp +++ b/src/gridcoin/voting/registry.cpp @@ -15,6 +15,8 @@ using namespace GRC; using LogFlags = BCLog::LogFlags; +extern bool fQtActive; + namespace { //! //! \brief Extract a poll title from a legacy vote contract. @@ -289,6 +291,11 @@ const std::vector& PollReference::Votes() const return m_votes; } +int64_t PollReference::Time() const +{ + return m_timestamp; +} + int64_t PollReference::Age(const int64_t now) const { return now - m_timestamp; @@ -460,6 +467,10 @@ void PollRegistry::AddPoll(const ContractContext& ctx) auto result_pair = m_polls_by_txid.emplace(ctx.m_tx.GetHash(), &poll_ref); poll_ref.m_ptxid = &result_pair.first->first; + + if (fQtActive && !poll_ref.Expired(GetAdjustedTime())) { + uiInterface.NewPollReceived(poll_ref.Time()); + } } } diff --git a/src/gridcoin/voting/registry.h b/src/gridcoin/voting/registry.h index 3a2d712f8a..c8c182c2cc 100644 --- a/src/gridcoin/voting/registry.h +++ b/src/gridcoin/voting/registry.h @@ -66,6 +66,13 @@ class PollReference //! const std::vector& Votes() const; + //! + //! \brief Get the timestamp of the poll transaction. + //! + //! \return Poll transaction timestamp in seconds. + //! + int64_t Time() const; + //! //! \brief Get the elapsed time since poll creation. //! diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bb4a853328..10f40c93b7 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -71,6 +71,15 @@ extern bool bGridcoinCoreInitComplete; static BitcoinGUI *guiref; static QSplashScreen *splashref; +static void RegisterMetaTypes() +{ + // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection + // (...Gridcoin has none yet...) + + // Register typedefs (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType) + qRegisterMetaType("int64_t"); +} + int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& optionsModel); static void SetupUIArgs(ArgsManager& argsman) @@ -288,6 +297,7 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); + RegisterMetaTypes(); QApplication app(argc, argv); #if defined(WIN32) && defined(QT_GUI) diff --git a/src/qt/voting/votingmodel.cpp b/src/qt/voting/votingmodel.cpp index dbc83d2764..8c51db0031 100644 --- a/src/qt/voting/votingmodel.cpp +++ b/src/qt/voting/votingmodel.cpp @@ -11,15 +11,29 @@ #include "gridcoin/voting/poll.h" #include "gridcoin/voting/registry.h" #include "gridcoin/voting/result.h" +#include "qt/clientmodel.h" #include "qt/voting/votingmodel.h" #include "qt/walletmodel.h" #include "sync.h" +#include "ui_interface.h" using namespace GRC; +using LogFlags = BCLog::LogFlags; extern CCriticalSection cs_main; namespace { +//! +//! \brief Model callback bound to the \c NewPollReceived core signal. +//! +void NewPollReceived(VotingModel* model, int64_t poll_time) +{ + LogPrint(LogFlags::QT, "GUI: received NewPollReceived() core signal"); + + QMetaObject::invokeMethod(model, "handleNewPoll", Qt::QueuedConnection, + Q_ARG(int64_t, poll_time)); +} + std::optional BuildPollItem(const PollRegistry::Sequence::Iterator& iter) { const PollResultOption result = PollResult::BuildFor(iter->Ref()); @@ -67,14 +81,29 @@ std::optional BuildPollItem(const PollRegistry::Sequence::Iterator& it // Class: VotingModel // ----------------------------------------------------------------------------- -VotingModel::VotingModel(WalletModel& wallet_model) +VotingModel::VotingModel(ClientModel& client_model, WalletModel& wallet_model) : m_registry(GetPollRegistry()) + , m_client_model(client_model) , m_wallet_model(wallet_model) + , m_last_poll_time(0) { + subscribeToCoreSignals(); + + // The voting model is constructed after core init finishes. Remember the + // time of the most recent active poll found on start-up to avoid showing + // notifications for these if the node reorganizes the chain: + { + LOCK(cs_main); + + for (const auto iter : m_registry.Polls().OnlyActive()) { + m_last_poll_time = std::max(m_last_poll_time, iter->Ref().Time()); + } + } } VotingModel::~VotingModel() { + unsubscribeFromCoreSignals(); } int VotingModel::minPollDurationDays() @@ -241,6 +270,27 @@ VotingResult VotingModel::sendVote( } } +void VotingModel::subscribeToCoreSignals() +{ + uiInterface.NewPollReceived.connect(boost::bind(NewPollReceived, this, boost::placeholders::_1)); +} + +void VotingModel::unsubscribeFromCoreSignals() +{ + uiInterface.NewPollReceived.disconnect(boost::bind(NewPollReceived, this, boost::placeholders::_1)); +} + +void VotingModel::handleNewPoll(int64_t poll_time) +{ + if (poll_time <= m_last_poll_time || m_client_model.inInitialBlockDownload()) { + return; + } + + m_last_poll_time = poll_time; + + emit newPollReceived(); +} + // ----------------------------------------------------------------------------- // Class: VoteResultItem // ----------------------------------------------------------------------------- diff --git a/src/qt/voting/votingmodel.h b/src/qt/voting/votingmodel.h index dfa6263bf0..6a6ffb4e4c 100644 --- a/src/qt/voting/votingmodel.h +++ b/src/qt/voting/votingmodel.h @@ -22,6 +22,7 @@ QT_BEGIN_NAMESPACE class QStringList; QT_END_NAMESPACE +class ClientModel; class uint256; class WalletModel; @@ -88,7 +89,7 @@ class VotingModel : public QObject Q_OBJECT public: - VotingModel(WalletModel& wallet_model); + VotingModel(ClientModel& client_model, WalletModel& wallet_model); ~VotingModel(); static int minPollDurationDays(); @@ -115,9 +116,20 @@ class VotingModel : public QObject const QString& poll_id, const std::vector& choice_offsets) const; +signals: + void newPollReceived() const; + private: GRC::PollRegistry& m_registry; + ClientModel& m_client_model; WalletModel& m_wallet_model; + int64_t m_last_poll_time; + + void subscribeToCoreSignals(); + void unsubscribeFromCoreSignals(); + +private slots: + void handleNewPoll(int64_t poll_time); }; // VotingModel #endif // VOTING_VOTINGMODEL_H diff --git a/src/ui_interface.h b/src/ui_interface.h index 66dc144f5e..241979bc41 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -105,6 +105,9 @@ class CClientUIInterface /** Beacon changed */ boost::signals2::signal BeaconChanged; + /** New poll received **/ + boost::signals2::signal NewPollReceived; + /** * New, updated or cancelled alert. * @note called with lock cs_mapAlerts held. From 265b2c8e218b651f8150a60a74c4959dbf7a9af4 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:44 -0500 Subject: [PATCH 168/280] Create a "new poll" wizard This adds a wizard that will replace the old "create poll" dialog. It provides a more structured process for poll creation. The wizard will present the set of poll types to the user based on community-approved guidelines and request information or disable fields for the selected type. --- gridcoinresearch.pro | 15 + src/Makefile.qt.include | 29 +- src/gridcoin/voting/builders.cpp | 8 +- src/gridcoin/voting/builders.h | 8 +- src/qt/forms/voting/pollwizard.ui | 67 ++++ src/qt/forms/voting/pollwizarddetailspage.ui | 337 +++++++++++++++++++ src/qt/forms/voting/pollwizardprojectpage.ui | 235 +++++++++++++ src/qt/forms/voting/pollwizardsummarypage.ui | 162 +++++++++ src/qt/forms/voting/pollwizardtypepage.ui | 115 +++++++ src/qt/res/stylesheets/dark_stylesheet.qss | 114 ++++++- src/qt/res/stylesheets/light_stylesheet.qss | 103 +++++- src/qt/voting/poll_types.cpp | 69 ++++ src/qt/voting/poll_types.h | 34 ++ src/qt/voting/pollwizard.cpp | 33 ++ src/qt/voting/pollwizard.h | 39 +++ src/qt/voting/pollwizarddetailspage.cpp | 312 +++++++++++++++++ src/qt/voting/pollwizarddetailspage.h | 44 +++ src/qt/voting/pollwizardprojectpage.cpp | 113 +++++++ src/qt/voting/pollwizardprojectpage.h | 35 ++ src/qt/voting/pollwizardsummarypage.cpp | 42 +++ src/qt/voting/pollwizardsummarypage.h | 31 ++ src/qt/voting/pollwizardtypepage.cpp | 79 +++++ src/qt/voting/pollwizardtypepage.h | 38 +++ src/qt/voting/votingmodel.cpp | 11 +- src/qt/voting/votingmodel.h | 8 +- 25 files changed, 2047 insertions(+), 34 deletions(-) create mode 100644 src/qt/forms/voting/pollwizard.ui create mode 100644 src/qt/forms/voting/pollwizarddetailspage.ui create mode 100644 src/qt/forms/voting/pollwizardprojectpage.ui create mode 100644 src/qt/forms/voting/pollwizardsummarypage.ui create mode 100644 src/qt/forms/voting/pollwizardtypepage.ui create mode 100644 src/qt/voting/poll_types.cpp create mode 100644 src/qt/voting/poll_types.h create mode 100644 src/qt/voting/pollwizard.cpp create mode 100644 src/qt/voting/pollwizard.h create mode 100644 src/qt/voting/pollwizarddetailspage.cpp create mode 100644 src/qt/voting/pollwizarddetailspage.h create mode 100644 src/qt/voting/pollwizardprojectpage.cpp create mode 100644 src/qt/voting/pollwizardprojectpage.h create mode 100644 src/qt/voting/pollwizardsummarypage.cpp create mode 100644 src/qt/voting/pollwizardsummarypage.h create mode 100644 src/qt/voting/pollwizardtypepage.cpp create mode 100644 src/qt/voting/pollwizardtypepage.h diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 3e3a43da63..67843dc658 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -186,6 +186,11 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/researcher/researcherwizardprojectspage.h \ src/qt/researcher/researcherwizardsummarypage.h \ src/qt/voting/polltablemodel.h \ + src/qt/voting/pollwizard.h \ + src/qt/voting/pollwizarddetailspage.h \ + src/qt/voting/pollwizardprojectpage.h \ + src/qt/voting/pollwizardsummarypage.h \ + src/qt/voting/pollwizardtypepage.h \ src/qt/voting/votingmodel.h \ src/qt/transactiontablemodel.h \ src/qt/addresstablemodel.h \ @@ -295,6 +300,11 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/researcher/researcherwizardprojectspage.cpp \ src/qt/researcher/researcherwizardsummarypage.cpp \ src/qt/voting/polltablemodel.cpp \ + src/qt/voting/pollwizard.cpp \ + src/qt/voting/pollwizarddetailspage.cpp \ + src/qt/voting/pollwizardprojectpage.cpp \ + src/qt/voting/pollwizardsummarypage.cpp \ + src/qt/voting/pollwizardtypepage.cpp \ src/qt/voting/votingmodel.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -399,6 +409,11 @@ FORMS += \ src/qt/forms/researcherwizardpoolsummarypage.ui \ src/qt/forms/researcherwizardprojectspage.ui \ src/qt/forms/researcherwizardsummarypage.ui \ + src/qt/forms/voting/pollwizard.ui \ + src/qt/forms/voting/pollwizarddetailspage.ui \ + src/qt/forms/voting/pollwizardprojectpage.ui \ + src/qt/forms/voting/pollwizardsummarypage.ui \ + src/qt/forms/voting/pollwizardtypepage.ui \ src/qt/forms/receivecoinspage.ui \ src/qt/forms/sendcoinsdialog.ui \ src/qt/forms/favoritespage.ui \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 5319ba3a83..99c12e956e 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -102,6 +102,11 @@ QT_FORMS_UI = \ qt/forms/researcherwizardsummarypage.ui \ qt/forms/sendcoinsdialog.ui \ qt/forms/transactiondescdialog.ui \ + qt/forms/voting/pollwizard.ui \ + qt/forms/voting/pollwizarddetailspage.ui \ + qt/forms/voting/pollwizardprojectpage.ui \ + qt/forms/voting/pollwizardsummarypage.ui \ + qt/forms/voting/pollwizardtypepage.ui \ qt/forms/askpassphrasedialog.ui \ qt/forms/sendcoinsentry.ui \ qt/forms/intro.ui @@ -168,6 +173,11 @@ QT_MOC_CPP = \ qt/researcher/moc_researcherwizardprojectspage.cpp \ qt/researcher/moc_researcherwizardsummarypage.cpp \ qt/voting/moc_polltablemodel.cpp \ + qt/voting/moc_pollwizard.cpp \ + qt/voting/moc_pollwizarddetailspage.cpp \ + qt/voting/moc_pollwizardprojectpage.cpp \ + qt/voting/moc_pollwizardsummarypage.cpp \ + qt/voting/moc_pollwizardtypepage.cpp \ qt/voting/moc_votingmodel.cpp GRIDCOIN_MM = \ @@ -178,7 +188,8 @@ GRIDCOIN_MM = \ QT_MOC = \ qt/intro.moc \ qt/overviewpage.moc \ - qt/rpcconsole.moc + qt/rpcconsole.moc \ + qt/voting/pollwizarddetailspage.moc QT_QRC_CPP = qt/qrc_bitcoin.cpp QT_QRC = qt/bitcoin.qrc @@ -251,9 +262,15 @@ GRIDCOINRESEARCH_QT_H = \ qt/transactiontablemodel.h \ qt/transactionview.h \ qt/upgradeqt.h \ - qt/votingdialog.h \ + qt/voting/poll_types.h \ qt/voting/polltablemodel.h \ + qt/voting/pollwizard.h \ + qt/voting/pollwizarddetailspage.h \ + qt/voting/pollwizardprojectpage.h \ + qt/voting/pollwizardsummarypage.h \ + qt/voting/pollwizardtypepage.h \ qt/voting/votingmodel.h \ + qt/votingdialog.h \ qt/walletmodel.h \ qt/winshutdownmonitor.h @@ -319,9 +336,15 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/transactiontablemodel.cpp \ qt/transactionview.cpp \ qt/upgradeqt.cpp \ - qt/votingdialog.cpp \ + qt/voting/poll_types.cpp \ qt/voting/polltablemodel.cpp \ + qt/voting/pollwizard.cpp \ + qt/voting/pollwizarddetailspage.cpp \ + qt/voting/pollwizardprojectpage.cpp \ + qt/voting/pollwizardsummarypage.cpp \ + qt/voting/pollwizardtypepage.cpp \ qt/voting/votingmodel.cpp \ + qt/votingdialog.cpp \ qt/walletmodel.cpp \ qt/winshutdownmonitor.cpp diff --git a/src/gridcoin/voting/builders.cpp b/src/gridcoin/voting/builders.cpp index 30acae1cfb..b0315ada20 100644 --- a/src/gridcoin/voting/builders.cpp +++ b/src/gridcoin/voting/builders.cpp @@ -840,7 +840,7 @@ void SelectFinalInputs(CWallet& wallet, CWalletTx& tx) // Global Functions // ----------------------------------------------------------------------------- -void GRC::SendPollContract(PollBuilder builder) +uint256 GRC::SendPollContract(PollBuilder builder) { std::pair result_pair; @@ -852,9 +852,11 @@ void GRC::SendPollContract(PollBuilder builder) if (!result_pair.second.empty()) { throw VotingError(result_pair.second); } + + return result_pair.first.GetHash(); } -void GRC::SendVoteContract(VoteBuilder builder) +uint256 GRC::SendVoteContract(VoteBuilder builder) { std::pair result_pair; @@ -866,6 +868,8 @@ void GRC::SendVoteContract(VoteBuilder builder) if (!result_pair.second.empty()) { throw VotingError(result_pair.second); } + + return result_pair.first.GetHash(); } // ----------------------------------------------------------------------------- diff --git a/src/gridcoin/voting/builders.h b/src/gridcoin/voting/builders.h index 3ba015c0e1..5d288cb092 100644 --- a/src/gridcoin/voting/builders.h +++ b/src/gridcoin/voting/builders.h @@ -307,10 +307,12 @@ class VoteBuilder //! \param builder An initialized poll builder instance to create the poll //! contract from. //! +//! \return The hash of the transaction that contains the new poll. +//! //! \throws VotingError If the constructed vote is malformed or the transaction //! fails to send. //! -void SendPollContract(PollBuilder builder); +uint256 SendPollContract(PollBuilder builder); //! //! \brief Send a transaction that contains a vote contract. @@ -322,8 +324,10 @@ void SendPollContract(PollBuilder builder); //! \param builder An initialized vote builder instance to create the vote //! contract from. //! +//! \return The hash of the transaction that contains the vote. +//! //! \throws VotingError If the constructed vote is malformed or the transaction //! fails to send. //! -void SendVoteContract(VoteBuilder builder); +uint256 SendVoteContract(VoteBuilder builder); } diff --git a/src/qt/forms/voting/pollwizard.ui b/src/qt/forms/voting/pollwizard.ui new file mode 100644 index 0000000000..bb321fad22 --- /dev/null +++ b/src/qt/forms/voting/pollwizard.ui @@ -0,0 +1,67 @@ + + + PollWizard + + + + 0 + 0 + 500 + 360 + + + + + 0 + 0 + + + + Create a Poll + + + true + + + true + + + QWizard::ClassicStyle + + + QWizard::NoBackButtonOnLastPage|QWizard::NoBackButtonOnStartPage|QWizard::NoCancelButtonOnLastPage + + + + + + + + + PollWizardTypePage + QWizardPage +
voting/pollwizardtypepage.h
+ 1 +
+ + PollWizardProjectPage + QWizardPage +
voting/pollwizardprojectpage.h
+ 1 +
+ + PollWizardDetailsPage + QWizardPage +
voting/pollwizarddetailspage.h
+ 1 +
+ + PollWizardSummaryPage + QWizardPage +
voting/pollwizardsummarypage.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/voting/pollwizarddetailspage.ui b/src/qt/forms/voting/pollwizarddetailspage.ui new file mode 100644 index 0000000000..4073797158 --- /dev/null +++ b/src/qt/forms/voting/pollwizarddetailspage.ui @@ -0,0 +1,337 @@ + + + PollWizardDetailsPage + + + + 0 + 0 + 630 + 480 + + + + + 0 + 0 + + + + + 9 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + Poll Details + + + + + + + Qt::Horizontal + + + + + + + Some fields are locked for the selected poll type. + + + + + + + true + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + 12 + + + 12 + + + + + Poll Type: + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Duration: + + + durationField + + + + + + + days + + + + + + + + + Title: + + + titleField + + + + + + + + + + Question: + + + questionField + + + + + + + + + + Discussion URL: + + + urlField + + + + + + + 3 + + + + + + + + A link to the main discussion thread on GitHub or Reddit. + + + true + + + + + + + + + Weight Type: + + + weightTypeList + + + + + + + + + + Response Type: + + + responseTypeList + + + + + + + + + + Choices: + + + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + A poll with a yes/no/abstain response type cannot include any additional custom choices. + + + true + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + true + + + QAbstractItemView::InternalMove + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + durationField + titleField + questionField + urlField + weightTypeList + responseTypeList + + + + diff --git a/src/qt/forms/voting/pollwizardprojectpage.ui b/src/qt/forms/voting/pollwizardprojectpage.ui new file mode 100644 index 0000000000..344cadc2e4 --- /dev/null +++ b/src/qt/forms/voting/pollwizardprojectpage.ui @@ -0,0 +1,235 @@ + + + PollWizardProjectPage + + + + 0 + 0 + 630 + 480 + + + + + 0 + 0 + + + + + 9 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + Project Listing Proposal + + + + + + + Qt::Horizontal + + + + + + + Add an unlisted project + + + + + + + Remove a listed project + + + + + + + + 0 + 0 + + + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + + + Proposals must follow community guidelines for validation. Please review the wiki and verify that the prequisites have been fulfilled: + + + true + + + + + + + 0 + + + + + + + + + 1 + 0 + + + + <a href="https://gridcoin.us/wiki/whitelist-process">https://gridcoin.us/wiki/whitelist-process</a> + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + Qt::Horizontal + + + + + + + 20 + + + + + Project Name: + + + projectNameField + + + + + + + + + + This project satisfies the Gridcoin listing criteria. + + + + + + + + + + + + + 0 + 1 + + + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + + + Choose a project to delist: + + + + + + + QAbstractItemView::NoEditTriggers + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + diff --git a/src/qt/forms/voting/pollwizardsummarypage.ui b/src/qt/forms/voting/pollwizardsummarypage.ui new file mode 100644 index 0000000000..08706cba11 --- /dev/null +++ b/src/qt/forms/voting/pollwizardsummarypage.ui @@ -0,0 +1,162 @@ + + + PollWizardSummaryPage + + + + 0 + 0 + 630 + 480 + + + + + 0 + 0 + + + + + 9 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 60 + + + + + + + + :/icons/round_green_check + + + + + + + Poll Created + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 24 + + + + + + + + The poll will activate with the next block. + + + Qt::AlignCenter + + + true + + + + + + + Qt::AlignCenter + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Copy ID + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/src/qt/forms/voting/pollwizardtypepage.ui b/src/qt/forms/voting/pollwizardtypepage.ui new file mode 100644 index 0000000000..4a56fb3273 --- /dev/null +++ b/src/qt/forms/voting/pollwizardtypepage.ui @@ -0,0 +1,115 @@ + + + PollWizardTypePage + + + + 0 + 0 + 630 + 480 + + + + + 0 + 0 + + + + + 9 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + Create a Poll + + + + + + + The Gridcoin community established guidelines for polls with requirements for each type. Please read the wiki for more information: + + + true + + + + + + + 0 + + + + + + + + + 1 + 0 + + + + <a href="https://gridcoin.us/wiki/voting.html">https://gridcoin.us/wiki/voting.html</a> + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + Qt::Horizontal + + + + + + + Choose a poll type: + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 91e7f4ea32..a49411c7db 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -209,26 +209,26 @@ QGroupBox::title { font-weight: bold; } -QListWidget, +QListView, QTableView, QTreeWidget { border: 0.065em solid rgb(58, 70, 94); border-radius: 0.26em; } -QListWidget::item, +QListView::item, QTableView::item { background-color: transparent; border: none; } -QListWidget::item { +QListView::item { height: 1.5em; padding-left: 0.5em; padding-right: 0.5em; } -QListWidget QScrollBar:horizontal, +QListView QScrollBar:horizontal, QTableView QScrollBar:horizontal, QTreeWidget QScrollBar:horizontal { border-top: 0.065em solid rgb(33, 44, 58); @@ -236,7 +236,7 @@ QTreeWidget QScrollBar:horizontal { border-top-right-radius: 0; } -QListWidget QScrollBar:vertical, +QListView QScrollBar:vertical, QTableView QScrollBar:vertical, QTreeWidget QScrollBar:vertical { border-left: 0.065em solid rgb(33, 44, 58); @@ -295,13 +295,10 @@ QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover, QLineEdit:focus, QPlainTextEdit:focus, -QSpinBox, +QSpinBox:focus, QSpinBox::up-button:hover, QSpinBox::down-button:hover, QTextEdit:focus, -QToolButton:hover, -QToolButton:focus, -QToolButton:selected, #headerFrame QLineEdit:focus { border-color: rgb(21, 126, 205); color: rgb(195, 199, 201); @@ -335,6 +332,8 @@ QDateTimeEdit::down-arrow { } QComboBox QAbstractItemView { + background: transparent; + border: none; selection-background-color: rgb(26, 145, 235); selection-color: white; } @@ -370,6 +369,33 @@ QToolButton { padding: 0.25em; } +QCommandLinkButton { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(18, 41, 58), stop: 1 rgb(14, 29, 42)); + border: 0.065em solid rgb(58, 70, 94); + color: rgb(195, 199, 201); +} + +QCommandLinkButton:hover, +QToolButton:hover, +QToolButton:focus, +QToolButton:selected { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(3, 43, 72), stop: 1 rgb(1, 20, 35)); + border-color: rgb(21, 126, 205); + color: rgb(225, 241, 253); +} + +QCommandLinkButton:pressed, +QToolButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(1, 20, 35), stop: 1 rgb(3, 43, 72)); +} + +QCommandLinkButton:checked, +QToolButton:checked { + background: rgb(3, 43, 72); + border-color: rgb(21, 126, 205); + color: rgb(225, 241, 253); +} + ClickLabel { color: palette(link); text-decoration: underline; @@ -708,6 +734,7 @@ NoResult #titleLabel { #listTransactions { background: none; + border: none; } #listTransactions::item { @@ -794,6 +821,75 @@ TransactionView #filterFrame { padding: 0.5em 1em; } +/* Voting page */ + +PollWizard #pageTitleLabel, +PollWizardTypePage #typeTextLabel { + font-weight: bold; + color: white; +} + +PollResultChoiceItem QProgressBar { + background: rgba(0, 0, 0, 0.2); + border: none; + border-radius: 0.25em; + min-height: 0.5em; + max-height: 0.5em; + padding: 0; +} + +PollWizardDetailsPage #pollTypeAlert { + background: rgba(255, 240, 0, 0.4); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: white; +} + +PollWizardDetailsPage #errorLabel { + background: rgba(255, 0, 0, 0.4); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: white; +} + +PollWizardDetailsPage #pollTypeLabel { + background: rgb(75, 70, 80); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: white; + font-weight: bold; +} + +PollWizardDetailsPage #choicesFrame { + border: 0.065em solid rgb(58, 70, 94); + border-radius: 0.26em; + padding: 0; +} + +PollWizardDetailsPage #choicesList { + border: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +PollWizardDetailsPage #choicesButtonFrame { + border-top: 0.065em solid rgb(58, 70, 94); + border-top-left-radius: 0; + border-top-right-radius: 0; + padding: 0; +} + +PollWizardDetailsPage #choicesButtonFrame QToolButton { + border: none; + border-right: 0.065em solid rgb(58, 70, 94); + border-radius: 0; + padding: 0.1em 0.25em; +} + +PollWizardDetailsPage #choicesButtonFrame QToolButton[firstChild=true] { + border-bottom-left-radius: 0.26em; +} + /* options dialog */ #OptionsDialog #statusLabel{ diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index aa269e8709..1b7774cbbd 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -207,7 +207,7 @@ QGroupBox::title { font-weight: bold; } -QListWidget, +QListView, QTableView, QTreeWidget { background-color: white; @@ -215,19 +215,19 @@ QTreeWidget { border-radius: 0.26em; } -QListWidget::item, +QListView::item, QTableView::item { background-color: transparent; border: none; } -QListWidget::item { +QListView::item { height: 1.5em; padding-left: 0.5em; padding-right: 0.5em; } -QListWidget QScrollBar:horizontal, +QListView QScrollBar:horizontal, QTableView QScrollBar:horizontal, QTreeWidget QScrollBar:horizontal { border-top: 0.065em solid rgb(194, 199, 205); @@ -235,7 +235,7 @@ QTreeWidget QScrollBar:horizontal { border-top-right-radius: 0; } -QListWidget QScrollBar:vertical, +QListView QScrollBar:vertical, QTableView QScrollBar:vertical, QTreeWidget QScrollBar:vertical { border-left: 0.065em solid rgb(194, 199, 205); @@ -295,10 +295,11 @@ QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover, QLineEdit:focus, QPlainTextEdit:focus, +QPushButton:checked, QPushButton:hover, QPushButton:focus, QPushButton:selected, -QSpinBox, +QSpinBox:focus, QSpinBox::up-button:hover, QSpinBox::down-button:hover, QTextEdit:focus, @@ -310,27 +311,29 @@ QToolButton:selected, color: rgb(88, 92, 107); } +QComboBox, +QPushButton, +QDoubleSpinBox::down-button, +QSpinBox::down-button, +QToolButton { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 245, 249)); +} + QComboBox:disabled, QDateTimeEdit:disabled, QDoubleSpinBox:disabled, +QDoubleSpinBox::down-button:disabled, QLineEdit:disabled, QPlainTextEdit:disabled, QPushButton:disabled, QSpinBox:disabled, +QSpinBox::down-button:disabled, QTextEdit:disabled, QToolButton:disabled { background-color: rgb(240, 240, 240); color: rgb(130, 130, 130); } -QComboBox, -QPushButton, -QDoubleSpinBox::down-button, -QSpinBox::down-button, -QToolButton { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 245, 249)); -} - QComboBox:hover, QComboBox:focus, QPushButton:hover, @@ -359,12 +362,14 @@ QDateTimeEdit::down-arrow { image: url(:/icons/light_chevron_down); } -QComboBox:selected { +QComboBox:selected, +QPushButton:checked { background-color: rgb(250, 240, 255); } QComboBox QAbstractItemView { - background-color: white; + background: transparent; + border: none; color: rgb(70, 73, 85); } @@ -378,6 +383,10 @@ QToolButton { color: rgb(97, 101, 118); } +QToolButton:hover { + color: rgb(120, 20, 255); +} + ClickLabel { color: palette(link); text-decoration: underline; @@ -705,6 +714,7 @@ NoResult #titleLabel { #listTransactions { background: none; + border: none; } #listTransactions::item { @@ -791,6 +801,67 @@ TransactionView #filterFrame { padding: 0.5em 1em; } +/* Voting page */ + +PollWizard #pageTitleLabel, +PollWizardTypePage #typeTextLabel { + color: rgb(43, 52, 69); + font-weight: bold; +} + +PollWizardDetailsPage #pollTypeAlert { + background: rgba(255, 240, 0, 0.6); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: black; +} + +PollWizardDetailsPage #errorLabel { + background: rgba(255, 0, 0, 0.4); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: black; +} + +PollWizardDetailsPage #pollTypeLabel { + background-color: rgb(75, 70, 80); + border-radius: 0.25em; + padding: 0.1em 0.2em; + color: white; + font-weight: bold; +} + +PollWizardDetailsPage #choicesFrame { + border: 0.065em solid rgb(194, 199, 205); + border-radius: 0.26em; + padding: 0; +} + +PollWizardDetailsPage #choicesList { + border: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +PollWizardDetailsPage #choicesButtonFrame { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(255, 255, 255), stop: 1 rgb(244, 245, 249)); + border-top: 0.065em solid rgb(194, 199, 205); + border-top-left-radius: 0; + border-top-right-radius: 0; + padding: 0; +} + +PollWizardDetailsPage #choicesButtonFrame QToolButton { + border: none; + border-right: 0.065em solid rgb(194, 199, 205); + border-radius: 0; + padding: 0.1em 0.25em; +} + +PollWizardDetailsPage #choicesButtonFrame QToolButton[firstChild=true] { + border-bottom-left-radius: 0.26em; +} + /* options dialog */ #OptionsDialog #statusLabel{ diff --git a/src/qt/voting/poll_types.cpp b/src/qt/voting/poll_types.cpp new file mode 100644 index 0000000000..8144ce6a45 --- /dev/null +++ b/src/qt/voting/poll_types.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/voting/poll_types.h" + +#include + +namespace { +struct PollTypeDefinition +{ + const char* m_name; + const char* m_description; + int m_min_duration_days; +}; + +const PollTypeDefinition g_poll_types[] = { + { "Unknown", "Unknown", 0 }, + { + QT_TRANSLATE_NOOP("PollTypes", "Project Listing"), + QT_TRANSLATE_NOOP("PollTypes", "Propose additions or removals of computing projects for research reward eligibility."), + 21, // min duration days + }, + { + QT_TRANSLATE_NOOP("PollTypes", "Protocol Development"), + QT_TRANSLATE_NOOP("PollTypes", "Propose a change to Gridcoin at the protocol level."), + 42, // min duration days + }, + { + QT_TRANSLATE_NOOP("PollTypes", "Governance"), + QT_TRANSLATE_NOOP("PollTypes", "Proposals related to Gridcoin management like poll requirements and funding."), + 21, // min duration days + }, + { + QT_TRANSLATE_NOOP("PollTypes", "Marketing"), + QT_TRANSLATE_NOOP("PollTypes", "Propose marketing initiatives like ad campaigns."), + 21, // min duration days + }, + { + QT_TRANSLATE_NOOP("PollTypes", "Outreach"), + QT_TRANSLATE_NOOP("PollTypes", "For polls about community representation, public relations, and communications."), + 21, // min duration days + }, + { + QT_TRANSLATE_NOOP("PollTypes", "Community"), + QT_TRANSLATE_NOOP("PollTypes", "For other initiatives related to the Gridcoin community."), + 21, // min duration days + }, + { + QT_TRANSLATE_NOOP("PollTypes", "Survey"), + QT_TRANSLATE_NOOP("PollTypes", "For opinion or casual polls without any particular requirements."), + 7, // min duration days + }, +}; +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: PollTypes +// ----------------------------------------------------------------------------- + +PollTypes::PollTypes() +{ + for (const auto& type : g_poll_types) { + emplace_back(); + back().m_name = QCoreApplication::translate("PollTypes", type.m_name); + back().m_description = QCoreApplication::translate("PollTypes", type.m_description); + back().m_min_duration_days = type.m_min_duration_days; + } +} diff --git a/src/qt/voting/poll_types.h b/src/qt/voting/poll_types.h new file mode 100644 index 0000000000..faf692a84f --- /dev/null +++ b/src/qt/voting/poll_types.h @@ -0,0 +1,34 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +class PollTypeItem +{ +public: + QString m_name; + QString m_description; + int m_min_duration_days; +}; + +class PollTypes : public std::vector +{ +public: + enum PollType + { + PollTypeUnknown, + PollTypeProject, + PollTypeDevelopment, + PollTypeGovernance, + PollTypeMarketing, + PollTypeOutreach, + PollTypeCommunity, + PollTypeSurvey, + }; + + PollTypes(); +}; diff --git a/src/qt/voting/pollwizard.cpp b/src/qt/voting/pollwizard.cpp new file mode 100644 index 0000000000..0b0d5e6b84 --- /dev/null +++ b/src/qt/voting/pollwizard.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_pollwizard.h" +#include "qt/voting/pollwizard.h" +#include "qt/voting/votingmodel.h" + +// ----------------------------------------------------------------------------- +// Class: PollWizard +// ----------------------------------------------------------------------------- + +PollWizard::PollWizard(VotingModel& voting_model, QWidget* parent) + : QWizard(parent) + , ui(new Ui::PollWizard) + , m_poll_types(new PollTypes()) +{ + ui->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose, true); + resize(GRC::ScaleSize(this, 670, 580)); + + ui->typePage->setPollTypes(m_poll_types.get()); + ui->projectPage->setModel(&voting_model); + ui->detailsPage->setModel(&voting_model); + ui->detailsPage->setPollTypes(m_poll_types.get()); +} + +PollWizard::~PollWizard() +{ + delete ui; +} diff --git a/src/qt/voting/pollwizard.h b/src/qt/voting/pollwizard.h new file mode 100644 index 0000000000..4c054644e1 --- /dev/null +++ b/src/qt/voting/pollwizard.h @@ -0,0 +1,39 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLWIZARD_H +#define VOTING_POLLWIZARD_H + +#include +#include + +namespace Ui { +class PollWizard; +} + +class PollTypes; +class VotingModel; + +class PollWizard : public QWizard +{ + Q_OBJECT + +public: + enum Pages + { + PageType, + PageProject, + PageDetails, + PageSummary, + }; + + explicit PollWizard(VotingModel& voting_model, QWidget* parent = nullptr); + ~PollWizard(); + +private: + Ui::PollWizard* ui; + std::unique_ptr m_poll_types; +}; + +#endif // VOTING_POLLWIZARD_H diff --git a/src/qt/voting/pollwizarddetailspage.cpp b/src/qt/voting/pollwizarddetailspage.cpp new file mode 100644 index 0000000000..c3b29c9f1a --- /dev/null +++ b/src/qt/voting/pollwizarddetailspage.cpp @@ -0,0 +1,312 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/bitcoinunits.h" +#include "qt/decoration.h" +#include "qt/forms/voting/ui_pollwizarddetailspage.h" +#include "qt/optionsmodel.h" +#include "qt/voting/pollwizard.h" +#include "qt/voting/pollwizarddetailspage.h" +#include "qt/voting/votingmodel.h" + +#include +#include +#include + +namespace { +//! +//! \brief Applies custom appearance and behavior to items in the poll choices +//! editor. +//! +class ChoicesListDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + ChoicesListDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) + { + } + + QWidget* createEditor( + QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const override + { + QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index); + + if (QLineEdit* line_edit = qobject_cast(editor)) { + line_edit->setMaxLength(VotingModel::maxPollChoiceLabelLength()); + } + + return editor; + } + +private: + void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override + { + QStyledItemDelegate::initStyleOption(option, index); + + // Display a row number before each choice label: + option->text = QStringLiteral("%1. %2").arg(index.row() + 1).arg(option->text); + } +}; // ChoicesListDelegate + +//! +//! \brief Provides for QWizardPage::registerField() without a real widget. +//! +struct DummyField : public QWidget +{ + DummyField(QWidget* parent = nullptr) : QWidget(parent) { hide(); } +}; +} // Anonymous namespace + +//! +//! \brief Manages the set of choices for the poll choices editor. +//! +class ChoicesListModel : public QStringListModel +{ + Q_OBJECT + +public: + ChoicesListModel(QObject* parent = nullptr) : QStringListModel(parent) + { + } + + bool isComplete(const int response_type) const + { + if (response_type == 0) { + return true; + } + + return rowCount(QModelIndex()) >= 2; + } + + QModelIndex addItem() + { + const int row = rowCount(QModelIndex()); + + if (!insertRows(row, 1)) { + return QModelIndex(); + } + + return index(row); + } + + void removeItem(const QModelIndex& index) + { + if (index.isValid()) { + removeRows(index.row(), 1); + } + + emit completeChanged(); + } + + bool setData(const QModelIndex& index, const QVariant& value, int role) override + { + emit completeChanged(); + + return QStringListModel::setData(index, value.toString().trimmed(), role); + } + + Qt::ItemFlags flags(const QModelIndex& index) const override + { + if (!index.isValid()) { + return QStringListModel::flags(index) | Qt::ItemIsDropEnabled; + } + + return QStringListModel::flags(index) & ~Qt::ItemIsDropEnabled; + } + +signals: + void completeChanged(); +}; // ChoicesListModel + +// ----------------------------------------------------------------------------- +// Class: PollWizardDetailsPage +// ----------------------------------------------------------------------------- + +PollWizardDetailsPage::PollWizardDetailsPage(QWidget* parent) + : QWizardPage(parent) + , ui(new Ui::PollWizardDetailsPage) + , m_poll_types(nullptr) + , m_choices_model(new ChoicesListModel(this)) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->pageTitleLabel, 14); + + setCommitPage(true); + setButtonText(QWizard::CommitButton, tr("Create Poll")); + + registerField("title*", ui->titleField); + registerField("durationDays", ui->durationField); + registerField("question", ui->questionField); + registerField("url*", ui->urlField); + registerField("weightType", ui->weightTypeList); + registerField("responseType", ui->responseTypeList); + registerField("txid", new DummyField(this), "", ""); + + ui->durationField->setMinimum(VotingModel::minPollDurationDays()); + ui->durationField->setMaximum(VotingModel::maxPollDurationDays()); + ui->titleField->setMaxLength(VotingModel::maxPollTitleLength()); + ui->questionField->setMaxLength(VotingModel::maxPollQuestionLength()); + ui->urlField->setMaxLength(VotingModel::maxPollUrlLength()); + + ui->weightTypeList->addItem(tr("Balance")); + ui->weightTypeList->addItem(tr("Magnitude+Balance")); + + ui->responseTypeList->addItem(tr("Yes/No/Abstain")); + ui->responseTypeList->addItem(tr("Single Choice")); + ui->responseTypeList->addItem(tr("Multiple Choice")); + + ChoicesListDelegate* choices_delegate = new ChoicesListDelegate(this); + + ui->choicesList->setModel(m_choices_model.get()); + ui->choicesList->setItemDelegate(choices_delegate); + ui->choicesFrame->hide(); + ui->editChoiceButton->hide(); + ui->removeChoiceButton->hide(); + + connect( + ui->responseTypeList, QOverload::of(&QComboBox::currentIndexChanged), + [this](int index) { + ui->choicesFrame->setVisible(index > 0); + ui->standardChoicesLabel->setVisible(index == 0); + emit completeChanged(); + }); + connect( + ui->choicesList->selectionModel(), &QItemSelectionModel::selectionChanged, + [this](const QItemSelection& selected, const QItemSelection& deselected) { + Q_UNUSED(deselected); + ui->editChoiceButton->setVisible(!selected.isEmpty()); + ui->removeChoiceButton->setVisible(!selected.isEmpty()); + }); + connect(ui->addChoiceButton, &QAbstractButton::clicked, [this]() { + ui->choicesList->edit(m_choices_model->addItem()); + ui->choicesList->scrollToBottom(); + }); + connect(ui->editChoiceButton, &QAbstractButton::clicked, [this]() { + ui->choicesList->edit(ui->choicesList->selectionModel()->selectedIndexes().first()); + }); + connect(ui->removeChoiceButton, &QAbstractButton::clicked, [this]() { + m_choices_model->removeItem(ui->choicesList->selectionModel()->selectedIndexes().first()); + }); + connect(m_choices_model.get(), &ChoicesListModel::completeChanged, [this]() { + emit completeChanged(); + }); +} + +PollWizardDetailsPage::~PollWizardDetailsPage() +{ + delete ui; +} + +void PollWizardDetailsPage::setModel(VotingModel* voting_model) +{ + m_voting_model = voting_model; + + updateIcons(); +} + +void PollWizardDetailsPage::setPollTypes(const PollTypes* const poll_types) +{ + m_poll_types = poll_types; +} + +void PollWizardDetailsPage::initializePage() +{ + if (!m_poll_types) { + return; + } + + ui->errorLabel->hide(); + + const int type_id = field("pollType").toInt(); + const PollTypeItem& poll_type = (*m_poll_types)[type_id]; + + ui->pollTypeLabel->setText(poll_type.m_name); + ui->durationField->setMinimum(poll_type.m_min_duration_days); + ui->durationField->setValue(poll_type.m_min_duration_days); + + if (type_id != PollTypes::PollTypeSurvey) { + ui->pollTypeAlert->show(); + ui->weightTypeList->setCurrentIndex(1); // Magnitude+Balance + ui->weightTypeList->setDisabled(true); + } else { + ui->pollTypeAlert->hide(); + ui->weightTypeList->setEnabled(true); + } + + if (type_id == PollTypes::PollTypeProject) { + ui->titleField->setText(QStringLiteral("[%1] %2") + .arg(poll_type.m_name) + .arg(field("projectPollTitle").toString())); + ui->responseTypeList->setCurrentIndex(0); // Yes/No/Abstain + ui->responseTypeList->setDisabled(true); + } else { + ui->responseTypeList->setEnabled(true); + } +} + +bool PollWizardDetailsPage::validatePage() +{ + if (!m_voting_model) { + return false; + } + + const CAmount burn_fee = m_voting_model->estimatePollFee(); + const QMessageBox::StandardButton pressed = QMessageBox::question( + this, + tr("Create Poll"), + tr("This poll will cost %1 plus a transaction fee. Continue?") + .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, burn_fee)) + ); + + if (pressed != QMessageBox::Yes) { + return false; + } + + const VotingResult result = m_voting_model->sendPoll( + field("title").toString(), + field("durationDays").toInt(), + field("question").toString(), + field("url").toString(), + // The dropdown list only contains non-deprecated weight type options + // which start from offset 2: + field("weightType").toInt() + 2, + field("responseType").toInt() + 1, + m_choices_model->stringList()); + + if (!result.ok()) { + ui->errorLabel->setText(result.error()); + ui->errorLabel->show(); + + return false; + } + + setField("txid", result.txid()); + + return true; +} + +bool PollWizardDetailsPage::isComplete() const +{ + return QWizardPage::isComplete() + && m_choices_model->isComplete(field("responseType").toInt()); +} + +void PollWizardDetailsPage::updateIcons() +{ + if (!m_voting_model) { + return; + } + + const QString theme = m_voting_model->getOptionsModel().getCurrentStyle(); + + ui->addChoiceButton->setIcon(QIcon(":/icons/" + theme + "_add")); + ui->editChoiceButton->setIcon(QIcon(":/icons/" + theme + "_edit")); + ui->removeChoiceButton->setIcon(QIcon(":/icons/" + theme + "_remove")); +} + +#include "qt/voting/pollwizarddetailspage.moc" diff --git a/src/qt/voting/pollwizarddetailspage.h b/src/qt/voting/pollwizarddetailspage.h new file mode 100644 index 0000000000..2992f9bd7a --- /dev/null +++ b/src/qt/voting/pollwizarddetailspage.h @@ -0,0 +1,44 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLWIZARDDETAILSPAGE_H +#define VOTING_POLLWIZARDDETAILSPAGE_H + +#include +#include + +namespace Ui { +class PollWizardDetailsPage; +} + +class ChoicesListModel; +class PollTypes; +class VotingModel; + +class PollWizardDetailsPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit PollWizardDetailsPage(QWidget* parent = nullptr); + ~PollWizardDetailsPage(); + + void setModel(VotingModel* voting_model); + void setPollTypes(const PollTypes* const poll_types); + + void initializePage() override; + bool validatePage() override; + bool isComplete() const override; + +private: + Ui::PollWizardDetailsPage* ui; + VotingModel* m_voting_model; + const PollTypes* m_poll_types; + std::unique_ptr m_choices_model; + +private slots: + void updateIcons(); +}; + +#endif // VOTING_POLLWIZARDDETAILSPAGE_H diff --git a/src/qt/voting/pollwizardprojectpage.cpp b/src/qt/voting/pollwizardprojectpage.cpp new file mode 100644 index 0000000000..87876718a2 --- /dev/null +++ b/src/qt/voting/pollwizardprojectpage.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_pollwizardprojectpage.h" +#include "qt/voting/pollwizard.h" +#include "qt/voting/pollwizardprojectpage.h" +#include "qt/voting/votingmodel.h" + +#include + +// ----------------------------------------------------------------------------- +// Class: PollWizardProjectPage +// ----------------------------------------------------------------------------- + +PollWizardProjectPage::PollWizardProjectPage(QWidget* parent) + : QWizardPage(parent) + , ui(new Ui::PollWizardProjectPage) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->pageTitleLabel, 14); + + // The asterisk denotes a mandatory field: + registerField("projectPollTitle*", ui->projectNameField); + + ui->addWidget->hide(); + ui->removeWidget->hide(); + + QStringListModel* project_names_model = new QStringListModel(this); + ui->projectsList->setModel(project_names_model); + + connect(ui->addRadioButton, &QAbstractButton::toggled, [this](bool checked) { + if (!checked) { + ui->criteriaCheckbox->setChecked(false); + } + + ui->addWidget->setVisible(checked); + setField("projectPollTitle", QVariant()); + emit completeChanged(); + }); + connect(ui->removeRadioButton, &QAbstractButton::toggled, [=](bool checked) { + if (checked && m_voting_model) { + project_names_model->setStringList(m_voting_model->getActiveProjectNames()); + } + + ui->removeWidget->setVisible(checked); + setField("projectPollTitle", QVariant()); + emit completeChanged(); + }); + connect(ui->criteriaCheckbox, &QAbstractButton::toggled, [this](bool checked) { + Q_UNUSED(checked); + emit completeChanged(); + }); + connect(ui->projectsList->selectionModel(), &QItemSelectionModel::selectionChanged, + [=](const QItemSelection& selected, const QItemSelection& deselected) { + Q_UNUSED(deselected); + + if (!selected.isEmpty()) { + const QModelIndex index = selected.indexes().first(); + setField("projectPollTitle", project_names_model->data(index).toString()); + } + + emit completeChanged(); + }); +} + +PollWizardProjectPage::~PollWizardProjectPage() +{ + delete ui; +} + +void PollWizardProjectPage::setModel(VotingModel* voting_model) +{ + m_voting_model = voting_model; +} + +void PollWizardProjectPage::initializePage() +{ + ui->criteriaCheckbox->setChecked(false); + ui->projectsList->selectionModel()->clearSelection(); +} + +bool PollWizardProjectPage::validatePage() +{ + const QString project_name = field("projectPollTitle").toString(); + + if (ui->addRadioButton->isChecked()) { + setField("projectPollTitle", tr("Add %1").arg(project_name)); + } else { + setField("projectPollTitle", tr("Remove %1").arg(project_name)); + } + + return true; +} + +bool PollWizardProjectPage::isComplete() const +{ + if (!QWizardPage::isComplete()) { + return false; + } + + if (ui->addRadioButton->isChecked()) { + return ui->criteriaCheckbox->isChecked(); + } + + if (ui->removeRadioButton->isChecked()) { + return true; + } + + return false; +} diff --git a/src/qt/voting/pollwizardprojectpage.h b/src/qt/voting/pollwizardprojectpage.h new file mode 100644 index 0000000000..e9ad67362b --- /dev/null +++ b/src/qt/voting/pollwizardprojectpage.h @@ -0,0 +1,35 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLWIZARDPROJECTPAGE_H +#define VOTING_POLLWIZARDPROJECTPAGE_H + +#include + +namespace Ui { +class PollWizardProjectPage; +} + +class VotingModel; + +class PollWizardProjectPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit PollWizardProjectPage(QWidget* parent = nullptr); + ~PollWizardProjectPage(); + + void setModel(VotingModel* voting_model); + + void initializePage() override; + bool validatePage() override; + bool isComplete() const override; + +private: + Ui::PollWizardProjectPage* ui; + VotingModel* m_voting_model; +}; + +#endif // VOTING_POLLWIZARDPROJECTPAGE_H diff --git a/src/qt/voting/pollwizardsummarypage.cpp b/src/qt/voting/pollwizardsummarypage.cpp new file mode 100644 index 0000000000..a20b028983 --- /dev/null +++ b/src/qt/voting/pollwizardsummarypage.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_pollwizardsummarypage.h" +#include "qt/voting/pollwizard.h" +#include "qt/voting/pollwizardsummarypage.h" +#include "qt/voting/votingmodel.h" + +#include + +// ----------------------------------------------------------------------------- +// Class: PollWizardSummaryPage +// ----------------------------------------------------------------------------- + +PollWizardSummaryPage::PollWizardSummaryPage(QWidget* parent) + : QWizardPage(parent) + , ui(new Ui::PollWizardSummaryPage) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->pageTitleLabel, 14); + GRC::ScaleFontPointSize(ui->pollTitleLabel, 12); + GRC::ScaleFontPointSize(ui->pollIdLabel, 8); +} + +PollWizardSummaryPage::~PollWizardSummaryPage() +{ + delete ui; +} + +void PollWizardSummaryPage::initializePage() +{ + ui->pollTitleLabel->setText(field("title").toString()); + ui->pollIdLabel->setText(field("txid").toString()); +} + +void PollWizardSummaryPage::on_copyToClipboardButton_clicked() const +{ + QApplication::clipboard()->setText(field("txid").toString()); +} diff --git a/src/qt/voting/pollwizardsummarypage.h b/src/qt/voting/pollwizardsummarypage.h new file mode 100644 index 0000000000..1917ca588b --- /dev/null +++ b/src/qt/voting/pollwizardsummarypage.h @@ -0,0 +1,31 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLWIZARDSUMMARYPAGE_H +#define VOTING_POLLWIZARDSUMMARYPAGE_H + +#include + +namespace Ui { +class PollWizardSummaryPage; +} + +class PollWizardSummaryPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit PollWizardSummaryPage(QWidget* parent = nullptr); + ~PollWizardSummaryPage(); + + void initializePage() override; + +private: + Ui::PollWizardSummaryPage* ui; + +private slots: + void on_copyToClipboardButton_clicked() const; +}; + +#endif // VOTING_POLLWIZARDSUMMARYPAGE_H diff --git a/src/qt/voting/pollwizardtypepage.cpp b/src/qt/voting/pollwizardtypepage.cpp new file mode 100644 index 0000000000..7c3b4f32f6 --- /dev/null +++ b/src/qt/voting/pollwizardtypepage.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_pollwizardtypepage.h" +#include "qt/voting/pollwizard.h" +#include "qt/voting/pollwizardtypepage.h" +#include "qt/voting/votingmodel.h" + +#include +#include +#include + +// ----------------------------------------------------------------------------- +// Class: PollWizardTypePage +// ----------------------------------------------------------------------------- + +PollWizardTypePage::PollWizardTypePage(QWidget* parent) + : QWizardPage(parent) + , ui(new Ui::PollWizardTypePage) + , m_type_buttons(new QButtonGroup(this)) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->pageTitleLabel, 14); + GRC::ScaleFontPointSize(ui->typeTextLabel, 11); + + // QWizardPage::registerField() cannot bind to QButtonGroup because it + // expects a QWidget instance so we provide a proxy widget: + QSpinBox* type_proxy = new QSpinBox(this); + type_proxy->setVisible(false); + + registerField("pollType*", type_proxy); + setField("pollType", PollTypes::PollTypeUnknown); + + connect( + m_type_buttons, QOverload::of(&QButtonGroup::buttonClicked), + [=](QAbstractButton*) { type_proxy->setValue(m_type_buttons->checkedId()); }); +} + +PollWizardTypePage::~PollWizardTypePage() +{ + delete ui; + delete m_type_buttons; +} + +void PollWizardTypePage::setPollTypes(const PollTypes* const poll_types) +{ + const QIcon button_icon(":/icons/tx_contract_voting"); + + // Start with "i = 1" to skip PollTypes::PollTypeUnknown: + for (size_t i = 1, row = 0, column = 0; i < poll_types->size(); ++i) { + const PollTypeItem& poll_type = (*poll_types)[i]; + + QCommandLinkButton* button = new QCommandLinkButton(this); + button->setText(poll_type.m_name); + button->setDescription(poll_type.m_description); + button->setIcon(button_icon); + button->setCheckable(true); + GRC::ScaleFontPointSize(button, 9); + + ui->typesButtonLayout->addWidget(button, row, column); + m_type_buttons->addButton(button, i); + + row += column; + column = !column; + } +} + +int PollWizardTypePage::nextId() const +{ + switch (field("pollType").toInt()) { + case PollTypes::PollTypeProject: + return PollWizard::PageProject; + } + + return PollWizard::PageDetails; +} diff --git a/src/qt/voting/pollwizardtypepage.h b/src/qt/voting/pollwizardtypepage.h new file mode 100644 index 0000000000..cd12101d04 --- /dev/null +++ b/src/qt/voting/pollwizardtypepage.h @@ -0,0 +1,38 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLWIZARDTYPEPAGE_H +#define VOTING_POLLWIZARDTYPEPAGE_H + +#include + +namespace Ui { +class PollWizardTypePage; +} + +class PollTypes; + +QT_BEGIN_NAMESPACE +class QButtonGroup; +QT_END_NAMESPACE + +class PollWizardTypePage : public QWizardPage +{ + Q_OBJECT + +public: + explicit PollWizardTypePage(QWidget* parent = nullptr); + ~PollWizardTypePage(); + + void setPollTypes(const PollTypes* const poll_types); + + int nextId() const override; + +private: + Ui::PollWizardTypePage* ui; + const PollTypes* m_poll_types; + QButtonGroup* m_type_buttons; +}; + +#endif // VOTING_POLLWIZARDTYPEPAGE_H diff --git a/src/qt/voting/votingmodel.cpp b/src/qt/voting/votingmodel.cpp index 8c51db0031..f1b5e1a5d4 100644 --- a/src/qt/voting/votingmodel.cpp +++ b/src/qt/voting/votingmodel.cpp @@ -81,9 +81,13 @@ std::optional BuildPollItem(const PollRegistry::Sequence::Iterator& it // Class: VotingModel // ----------------------------------------------------------------------------- -VotingModel::VotingModel(ClientModel& client_model, WalletModel& wallet_model) +VotingModel::VotingModel( + ClientModel& client_model, + OptionsModel& options_model, + WalletModel& wallet_model) : m_registry(GetPollRegistry()) , m_client_model(client_model) + , m_options_model(options_model) , m_wallet_model(wallet_model) , m_last_poll_time(0) { @@ -156,6 +160,11 @@ int VotingModel::maxPollChoiceLabelLength() return Poll::Choice::MAX_LABEL_SIZE; } +OptionsModel& VotingModel::getOptionsModel() +{ + return m_options_model; +} + QStringList VotingModel::getActiveProjectNames() const { QStringList names; diff --git a/src/qt/voting/votingmodel.h b/src/qt/voting/votingmodel.h index 6a6ffb4e4c..1d6dabe4af 100644 --- a/src/qt/voting/votingmodel.h +++ b/src/qt/voting/votingmodel.h @@ -23,6 +23,7 @@ class QStringList; QT_END_NAMESPACE class ClientModel; +class OptionsModel; class uint256; class WalletModel; @@ -89,7 +90,10 @@ class VotingModel : public QObject Q_OBJECT public: - VotingModel(ClientModel& client_model, WalletModel& wallet_model); + VotingModel( + ClientModel& client_model, + OptionsModel& options_model, + WalletModel& wallet_model); ~VotingModel(); static int minPollDurationDays(); @@ -99,6 +103,7 @@ class VotingModel : public QObject static int maxPollQuestionLength(); static int maxPollChoiceLabelLength(); + OptionsModel& getOptionsModel(); QStringList getActiveProjectNames() const; std::vector buildPollTable(const GRC::PollFilterFlag flags) const; @@ -122,6 +127,7 @@ class VotingModel : public QObject private: GRC::PollRegistry& m_registry; ClientModel& m_client_model; + OptionsModel& m_options_model; WalletModel& m_wallet_model; int64_t m_last_poll_time; From f02a5f931f82d3796459485ef8576a4e279bb4e4 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:47 -0500 Subject: [PATCH 169/280] Overhaul GUI voting page design This updates the GUI voting page according to the proposed design mock ups: - Add a header bar with a poll title filter input - Add tabs for "active" and "completed" polls - Add a list view for polls and update the table view - Add a loading animation displayed when refreshing polls - Limit the max content width for large window sizes - Update colors, icons, layout, sizing, and spacing --- gridcoinresearch.pro | 23 +- src/Makefile.qt.include | 82 +- src/qt/bitcoin.cpp | 3 + src/qt/bitcoin.qrc | 21 + src/qt/bitcoingui.cpp | 13 +- src/qt/bitcoingui.h | 10 +- src/qt/forms/overviewpage.ui | 6 +- src/qt/forms/voting/pollcard.ui | 235 ++++ src/qt/forms/voting/pollcardview.ui | 167 +++ src/qt/forms/voting/polldetails.ui | 137 +++ src/qt/forms/voting/pollresultchoiceitem.ui | 106 ++ src/qt/forms/voting/pollresultdialog.ui | 157 +++ src/qt/forms/voting/polltab.ui | 135 ++ src/qt/forms/voting/votingpage.ui | 235 ++++ src/qt/noresult.cpp | 15 + src/qt/noresult.h | 10 + src/qt/res/icons/icons_dark/add.svg | 1 + src/qt/res/icons/icons_dark/create.svg | 1 + src/qt/res/icons/icons_dark/edit.svg | 1 + src/qt/res/icons/icons_dark/hamburger.svg | 1 + src/qt/res/icons/icons_dark/list_view.svg | 4 + src/qt/res/icons/icons_dark/refresh.svg | 1 + src/qt/res/icons/icons_dark/remove.svg | 1 + src/qt/res/icons/icons_dark/sort_asc.svg | 5 + src/qt/res/icons/icons_dark/table_view.svg | 3 + src/qt/res/icons/icons_dark/vote.svg | 1 + src/qt/res/icons/icons_light/add.svg | 1 + src/qt/res/icons/icons_light/create.svg | 1 + src/qt/res/icons/icons_light/edit.svg | 1 + src/qt/res/icons/icons_light/hamburger.svg | 1 + src/qt/res/icons/icons_light/list_view.svg | 4 + src/qt/res/icons/icons_light/refresh.svg | 1 + src/qt/res/icons/icons_light/remove.svg | 1 + src/qt/res/icons/icons_light/sort_asc.svg | 5 + src/qt/res/icons/icons_light/table_view.svg | 3 + src/qt/res/icons/icons_light/vote.svg | 1 + src/qt/res/icons/open_link.svg | 1 + src/qt/res/stylesheets/dark_stylesheet.qss | 114 +- src/qt/res/stylesheets/light_stylesheet.qss | 116 +- src/qt/voting/pollcard.cpp | 74 ++ src/qt/voting/pollcard.h | 38 + src/qt/voting/pollcardview.cpp | 144 +++ src/qt/voting/pollcardview.h | 55 + src/qt/voting/polldetails.cpp | 42 + src/qt/voting/polldetails.h | 30 + src/qt/voting/pollresultchoiceitem.cpp | 77 ++ src/qt/voting/pollresultchoiceitem.h | 32 + src/qt/voting/pollresultdialog.cpp | 77 ++ src/qt/voting/pollresultdialog.h | 28 + src/qt/voting/polltab.cpp | 292 +++++ src/qt/voting/polltab.h | 81 ++ src/qt/voting/votingpage.cpp | 166 +++ src/qt/voting/votingpage.h | 51 + src/qt/votingdialog.cpp | 1228 ------------------- src/qt/votingdialog.h | 295 ----- 55 files changed, 2781 insertions(+), 1553 deletions(-) create mode 100644 src/qt/forms/voting/pollcard.ui create mode 100644 src/qt/forms/voting/pollcardview.ui create mode 100644 src/qt/forms/voting/polldetails.ui create mode 100644 src/qt/forms/voting/pollresultchoiceitem.ui create mode 100644 src/qt/forms/voting/pollresultdialog.ui create mode 100644 src/qt/forms/voting/polltab.ui create mode 100644 src/qt/forms/voting/votingpage.ui create mode 100644 src/qt/res/icons/icons_dark/add.svg create mode 100644 src/qt/res/icons/icons_dark/create.svg create mode 100644 src/qt/res/icons/icons_dark/edit.svg create mode 100644 src/qt/res/icons/icons_dark/hamburger.svg create mode 100644 src/qt/res/icons/icons_dark/list_view.svg create mode 100644 src/qt/res/icons/icons_dark/refresh.svg create mode 100644 src/qt/res/icons/icons_dark/remove.svg create mode 100644 src/qt/res/icons/icons_dark/sort_asc.svg create mode 100644 src/qt/res/icons/icons_dark/table_view.svg create mode 100644 src/qt/res/icons/icons_dark/vote.svg create mode 100644 src/qt/res/icons/icons_light/add.svg create mode 100644 src/qt/res/icons/icons_light/create.svg create mode 100644 src/qt/res/icons/icons_light/edit.svg create mode 100644 src/qt/res/icons/icons_light/hamburger.svg create mode 100644 src/qt/res/icons/icons_light/list_view.svg create mode 100644 src/qt/res/icons/icons_light/refresh.svg create mode 100644 src/qt/res/icons/icons_light/remove.svg create mode 100644 src/qt/res/icons/icons_light/sort_asc.svg create mode 100644 src/qt/res/icons/icons_light/table_view.svg create mode 100644 src/qt/res/icons/icons_light/vote.svg create mode 100644 src/qt/res/icons/open_link.svg create mode 100644 src/qt/voting/pollcard.cpp create mode 100644 src/qt/voting/pollcard.h create mode 100644 src/qt/voting/pollcardview.cpp create mode 100644 src/qt/voting/pollcardview.h create mode 100644 src/qt/voting/polldetails.cpp create mode 100644 src/qt/voting/polldetails.h create mode 100644 src/qt/voting/pollresultchoiceitem.cpp create mode 100644 src/qt/voting/pollresultchoiceitem.h create mode 100644 src/qt/voting/pollresultdialog.cpp create mode 100644 src/qt/voting/pollresultdialog.h create mode 100644 src/qt/voting/polltab.cpp create mode 100644 src/qt/voting/polltab.h create mode 100644 src/qt/voting/votingpage.cpp create mode 100644 src/qt/voting/votingpage.h delete mode 100644 src/qt/votingdialog.cpp delete mode 100644 src/qt/votingdialog.h diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 67843dc658..4e94242a82 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -185,6 +185,12 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/researcher/researcherwizardpoolsummarypage.h \ src/qt/researcher/researcherwizardprojectspage.h \ src/qt/researcher/researcherwizardsummarypage.h \ + src/qt/voting/pollcard.h \ + src/qt/voting/pollcardview.h \ + src/qt/voting/polldetails.h \ + src/qt/voting/pollresultchoiceitem.h \ + src/qt/voting/pollresultdialog.h \ + src/qt/voting/polltab.h \ src/qt/voting/polltablemodel.h \ src/qt/voting/pollwizard.h \ src/qt/voting/pollwizarddetailspage.h \ @@ -192,6 +198,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/voting/pollwizardsummarypage.h \ src/qt/voting/pollwizardtypepage.h \ src/qt/voting/votingmodel.h \ + src/qt/voting/votingpage.h \ src/qt/transactiontablemodel.h \ src/qt/addresstablemodel.h \ src/qt/optionsdialog.h \ @@ -267,7 +274,6 @@ HEADERS += src/qt/bitcoingui.h \ src/protocol.h \ src/qt/notificator.h \ src/qt/qtipcserver.h \ - src/qt/votingdialog.h \ src/allocators.h \ src/ui_interface.h \ src/qt/rpcconsole.h \ @@ -299,6 +305,12 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/researcher/researcherwizardpoolsummarypage.cpp \ src/qt/researcher/researcherwizardprojectspage.cpp \ src/qt/researcher/researcherwizardsummarypage.cpp \ + src/qt/voting/pollcard.cpp \ + src/qt/voting/pollcardview.cpp \ + src/qt/voting/polldetails.cpp \ + src/qt/voting/pollresultchoiceitem.cpp \ + src/qt/voting/pollresultdialog.cpp \ + src/qt/voting/polltab.cpp \ src/qt/voting/polltablemodel.cpp \ src/qt/voting/pollwizard.cpp \ src/qt/voting/pollwizarddetailspage.cpp \ @@ -306,6 +318,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/voting/pollwizardsummarypage.cpp \ src/qt/voting/pollwizardtypepage.cpp \ src/qt/voting/votingmodel.cpp \ + src/qt/voting/votingpage.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ src/qt/optionsdialog.cpp \ @@ -319,7 +332,6 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/aboutdialog.cpp \ src/qt/editaddressdialog.cpp \ src/qt/bitcoinaddressvalidator.cpp \ - src/qt/votingdialog.cpp \ src/qt/diagnosticsdialog.cpp \ src/alert.cpp \ src/block.cpp \ @@ -409,11 +421,18 @@ FORMS += \ src/qt/forms/researcherwizardpoolsummarypage.ui \ src/qt/forms/researcherwizardprojectspage.ui \ src/qt/forms/researcherwizardsummarypage.ui \ + src/qt/forms/voting/pollcard.ui \ + src/qt/forms/voting/pollcardview.ui \ + src/qt/forms/voting/polldetails.ui \ + src/qt/forms/voting/pollresultchoiceitem.ui \ + src/qt/forms/voting/pollresultdialog.ui \ + src/qt/forms/voting/polltab.ui \ src/qt/forms/voting/pollwizard.ui \ src/qt/forms/voting/pollwizarddetailspage.ui \ src/qt/forms/voting/pollwizardprojectpage.ui \ src/qt/forms/voting/pollwizardsummarypage.ui \ src/qt/forms/voting/pollwizardtypepage.ui \ + src/qt/forms/voting/votingpage.ui \ src/qt/forms/receivecoinspage.ui \ src/qt/forms/sendcoinsdialog.ui \ src/qt/forms/favoritespage.ui \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 99c12e956e..8a1645f2e6 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -73,6 +73,8 @@ QT_TS = \ QT_FORMS_UI = \ qt/forms/aboutdialog.ui \ + qt/forms/addressbookpage.ui \ + qt/forms/askpassphrasedialog.ui \ qt/forms/coincontroldialog.ui \ qt/forms/consolidateunspentdialog.ui \ qt/forms/consolidateunspentwizard.ui \ @@ -80,13 +82,11 @@ QT_FORMS_UI = \ qt/forms/consolidateunspentwizardselectinputspage.ui \ qt/forms/consolidateunspentwizardsendpage.ui \ qt/forms/diagnosticsdialog.ui \ - qt/forms/favoritespage.ui \ - qt/forms/optionsdialog.ui \ - qt/forms/rpcconsole.ui \ - qt/forms/signverifymessagedialog.ui \ - qt/forms/addressbookpage.ui \ qt/forms/editaddressdialog.ui \ + qt/forms/favoritespage.ui \ + qt/forms/intro.ui \ qt/forms/noresult.ui \ + qt/forms/optionsdialog.ui \ qt/forms/overviewpage.ui \ qt/forms/receivecoinspage.ui \ qt/forms/researcherwizard.ui \ @@ -94,22 +94,29 @@ QT_FORMS_UI = \ qt/forms/researcherwizardbeaconpage.ui \ qt/forms/researcherwizardemailpage.ui \ qt/forms/researcherwizardinvestorpage.ui \ - qt/forms/researcherwizardmodepage.ui \ qt/forms/researcherwizardmodedetailpage.ui \ + qt/forms/researcherwizardmodepage.ui \ qt/forms/researcherwizardpoolpage.ui \ qt/forms/researcherwizardpoolsummarypage.ui \ qt/forms/researcherwizardprojectspage.ui \ qt/forms/researcherwizardsummarypage.ui \ + qt/forms/rpcconsole.ui \ qt/forms/sendcoinsdialog.ui \ + qt/forms/sendcoinsentry.ui \ + qt/forms/signverifymessagedialog.ui \ qt/forms/transactiondescdialog.ui \ + qt/forms/voting/pollcard.ui \ + qt/forms/voting/pollcardview.ui \ + qt/forms/voting/polldetails.ui \ + qt/forms/voting/pollresultchoiceitem.ui \ + qt/forms/voting/pollresultdialog.ui \ + qt/forms/voting/polltab.ui \ qt/forms/voting/pollwizard.ui \ qt/forms/voting/pollwizarddetailspage.ui \ qt/forms/voting/pollwizardprojectpage.ui \ qt/forms/voting/pollwizardsummarypage.ui \ qt/forms/voting/pollwizardtypepage.ui \ - qt/forms/askpassphrasedialog.ui \ - qt/forms/sendcoinsentry.ui \ - qt/forms/intro.ui + qt/forms/voting/votingpage.ui QT_MOC_CPP = \ qt/moc_aboutdialog.cpp \ @@ -157,7 +164,6 @@ QT_MOC_CPP = \ qt/moc_transactionfilterproxy.cpp \ qt/moc_transactiontablemodel.cpp \ qt/moc_transactionview.cpp \ - qt/moc_votingdialog.cpp \ qt/moc_walletmodel.cpp \ qt/researcher/moc_projecttablemodel.cpp \ qt/researcher/moc_researchermodel.cpp \ @@ -166,19 +172,26 @@ QT_MOC_CPP = \ qt/researcher/moc_researcherwizardbeaconpage.cpp \ qt/researcher/moc_researcherwizardemailpage.cpp \ qt/researcher/moc_researcherwizardinvestorpage.cpp \ - qt/researcher/moc_researcherwizardmodepage.cpp \ qt/researcher/moc_researcherwizardmodedetailpage.cpp \ + qt/researcher/moc_researcherwizardmodepage.cpp \ qt/researcher/moc_researcherwizardpoolpage.cpp \ qt/researcher/moc_researcherwizardpoolsummarypage.cpp \ qt/researcher/moc_researcherwizardprojectspage.cpp \ qt/researcher/moc_researcherwizardsummarypage.cpp \ + qt/voting/moc_pollcard.cpp \ + qt/voting/moc_pollcardview.cpp \ + qt/voting/moc_polldetails.cpp \ + qt/voting/moc_pollresultchoiceitem.cpp \ + qt/voting/moc_pollresultdialog.cpp \ + qt/voting/moc_polltab.cpp \ qt/voting/moc_polltablemodel.cpp \ qt/voting/moc_pollwizard.cpp \ qt/voting/moc_pollwizarddetailspage.cpp \ qt/voting/moc_pollwizardprojectpage.cpp \ qt/voting/moc_pollwizardsummarypage.cpp \ qt/voting/moc_pollwizardtypepage.cpp \ - qt/voting/moc_votingmodel.cpp + qt/voting/moc_votingmodel.cpp \ + qt/voting/moc_votingpage.cpp GRIDCOIN_MM = \ qt/macdockiconhandler.mm \ @@ -189,7 +202,9 @@ QT_MOC = \ qt/intro.moc \ qt/overviewpage.moc \ qt/rpcconsole.moc \ - qt/voting/pollwizarddetailspage.moc + qt/voting/polltab.moc \ + qt/voting/pollwizarddetailspage.moc \ + qt/voting/votingpage.moc QT_QRC_CPP = qt/qrc_bitcoin.cpp QT_QRC = qt/bitcoin.qrc @@ -244,8 +259,8 @@ GRIDCOINRESEARCH_QT_H = \ qt/researcher/researcherwizardbeaconpage.h \ qt/researcher/researcherwizardemailpage.h \ qt/researcher/researcherwizardinvestorpage.h \ - qt/researcher/researcherwizardmodepage.h \ qt/researcher/researcherwizardmodedetailpage.h \ + qt/researcher/researcherwizardmodepage.h \ qt/researcher/researcherwizardpoolpage.h \ qt/researcher/researcherwizardpoolsummarypage.h \ qt/researcher/researcherwizardprojectspage.h \ @@ -263,6 +278,12 @@ GRIDCOINRESEARCH_QT_H = \ qt/transactionview.h \ qt/upgradeqt.h \ qt/voting/poll_types.h \ + qt/voting/pollcard.h \ + qt/voting/pollcardview.h \ + qt/voting/polldetails.h \ + qt/voting/pollresultchoiceitem.h \ + qt/voting/pollresultdialog.h \ + qt/voting/polltab.h \ qt/voting/polltablemodel.h \ qt/voting/pollwizard.h \ qt/voting/pollwizarddetailspage.h \ @@ -270,7 +291,7 @@ GRIDCOINRESEARCH_QT_H = \ qt/voting/pollwizardsummarypage.h \ qt/voting/pollwizardtypepage.h \ qt/voting/votingmodel.h \ - qt/votingdialog.h \ + qt/voting/votingpage.h \ qt/walletmodel.h \ qt/winshutdownmonitor.h @@ -318,8 +339,8 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/researcher/researcherwizardbeaconpage.cpp \ qt/researcher/researcherwizardemailpage.cpp \ qt/researcher/researcherwizardinvestorpage.cpp \ - qt/researcher/researcherwizardmodepage.cpp \ qt/researcher/researcherwizardmodedetailpage.cpp \ + qt/researcher/researcherwizardmodepage.cpp \ qt/researcher/researcherwizardpoolpage.cpp \ qt/researcher/researcherwizardpoolsummarypage.cpp \ qt/researcher/researcherwizardprojectspage.cpp \ @@ -337,6 +358,12 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/transactionview.cpp \ qt/upgradeqt.cpp \ qt/voting/poll_types.cpp \ + qt/voting/pollcard.cpp \ + qt/voting/pollcardview.cpp \ + qt/voting/polldetails.cpp \ + qt/voting/pollresultchoiceitem.cpp \ + qt/voting/pollresultdialog.cpp \ + qt/voting/polltab.cpp \ qt/voting/polltablemodel.cpp \ qt/voting/pollwizard.cpp \ qt/voting/pollwizarddetailspage.cpp \ @@ -344,7 +371,7 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/voting/pollwizardsummarypage.cpp \ qt/voting/pollwizardtypepage.cpp \ qt/voting/votingmodel.cpp \ - qt/votingdialog.cpp \ + qt/voting/votingpage.cpp \ qt/walletmodel.cpp \ qt/winshutdownmonitor.cpp @@ -381,6 +408,7 @@ RES_ICONS = \ qt/res/icons/menu_active.svg \ qt/res/icons/message.svg \ qt/res/icons/no_result.svg \ + qt/res/icons/open_link.svg \ qt/res/icons/qrcode.png \ qt/res/icons/quit.png \ qt/res/icons/remove.png \ @@ -404,6 +432,13 @@ RES_ICONS = \ qt/res/icons/warning.svg \ qt/res/icons/white_and_red_x.svg \ qt/res/icons/www.png \ + qt/res/icons/icons_light/add.svg \ + qt/res/icons/icons_light/create.svg \ + qt/res/icons/icons_light/edit.svg \ + qt/res/icons/icons_light/hamburger.svg \ + qt/res/icons/icons_light/list_view.svg \ + qt/res/icons/icons_light/refresh.svg \ + qt/res/icons/icons_light/remove.svg \ qt/res/icons/icons_light/search.svg \ qt/res/icons/icons_light/settings.svg \ qt/res/icons/icons_light/settings_action_needed.svg \ @@ -425,6 +460,7 @@ RES_ICONS = \ qt/res/icons/icons_light/sidebar_unlocked_inactive.svg \ qt/res/icons/icons_light/sidebar_voting_active.svg \ qt/res/icons/icons_light/sidebar_voting_inactive.svg \ + qt/res/icons/icons_light/sort_asc.svg \ qt/res/icons/icons_light/status_beacon_green.svg \ qt/res/icons/icons_light/status_beacon_gray.svg \ qt/res/icons/icons_light/status_beacon_red.svg \ @@ -447,6 +483,15 @@ RES_ICONS = \ qt/res/icons/icons_light/status_sync_done.svg \ qt/res/icons/icons_light/status_sync_stalled.svg \ qt/res/icons/icons_light/status_sync_syncing.svg \ + qt/res/icons/icons_light/table_view.svg \ + qt/res/icons/icons_light/vote.svg \ + qt/res/icons/icons_dark/add.svg \ + qt/res/icons/icons_dark/create.svg \ + qt/res/icons/icons_dark/edit.svg \ + qt/res/icons/icons_dark/hamburger.svg \ + qt/res/icons/icons_dark/list_view.svg \ + qt/res/icons/icons_dark/refresh.svg \ + qt/res/icons/icons_dark/remove.svg \ qt/res/icons/icons_dark/search.svg \ qt/res/icons/icons_dark/settings_action_needed.svg \ qt/res/icons/icons_dark/sidebar_favorites_active.svg \ @@ -467,6 +512,7 @@ RES_ICONS = \ qt/res/icons/icons_dark/sidebar_unlocked_inactive.svg \ qt/res/icons/icons_dark/sidebar_voting_active.svg \ qt/res/icons/icons_dark/sidebar_voting_inactive.svg \ + qt/res/icons/icons_dark/sort_asc.svg \ qt/res/icons/icons_dark/status_beacon_green.svg \ qt/res/icons/icons_dark/status_beacon_gray.svg \ qt/res/icons/icons_dark/status_beacon_red.svg \ @@ -489,7 +535,9 @@ RES_ICONS = \ qt/res/icons/icons_dark/status_sync_done.svg \ qt/res/icons/icons_dark/status_sync_stalled.svg \ qt/res/icons/icons_dark/status_sync_syncing.svg \ + qt/res/icons/icons_dark/table_view.svg \ qt/res/icons/icons_dark/transactions.svg \ + qt/res/icons/icons_dark/vote.svg \ qt/res/icons/icons_light/transactions.svg \ qt/res/icons/icons_light/chevron_down.svg \ qt/res/icons/icons_light/chevron_right.svg \ diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 10f40c93b7..b3375cb223 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -12,6 +12,7 @@ #include "clientmodel.h" #include "walletmodel.h" #include "researcher/researchermodel.h" +#include "voting/votingmodel.h" #include "optionsmodel.h" #include "guiutil.h" #include "qt/intro.h" @@ -632,10 +633,12 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt ClientModel clientModel(&optionsModel); WalletModel walletModel(pwalletMain, &optionsModel); ResearcherModel researcherModel; + VotingModel votingModel(clientModel, optionsModel, walletModel); window.setResearcherModel(&researcherModel); window.setClientModel(&clientModel); window.setWalletModel(&walletModel); + window.setVotingModel(&votingModel); // If -min option passed, start window minimized. if(gArgs.GetBoolArg("-min")) diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index d72c35728a..1e9e62e37e 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -88,12 +88,22 @@ res/icons/icons_light/status_encryption_unlocked.svg + res/icons/icons_light/add.svg res/icons/icons_light/chevron_down.svg res/icons/icons_light/chevron_right.svg res/icons/icons_light/chevron_up.svg + res/icons/icons_light/create.svg + res/icons/icons_light/edit.svg + res/icons/icons_light/hamburger.svg + res/icons/icons_light/list_view.svg + res/icons/icons_light/refresh.svg + res/icons/icons_light/remove.svg res/icons/icons_light/search.svg res/icons/icons_light/settings.svg res/icons/icons_light/settings_action_needed.svg + res/icons/icons_light/sort_asc.svg + res/icons/icons_light/table_view.svg + res/icons/icons_light/vote.svg + res/icons/icons_dark/add.svg res/icons/icons_dark/chevron_down.svg res/icons/icons_dark/chevron_right.svg res/icons/icons_dark/chevron_up.svg + res/icons/icons_dark/create.svg + res/icons/icons_dark/edit.svg + res/icons/icons_dark/hamburger.svg + res/icons/icons_dark/list_view.svg + res/icons/icons_dark/refresh.svg + res/icons/icons_dark/remove.svg res/icons/icons_dark/search.svg res/icons/icons_dark/settings_action_needed.svg + res/icons/icons_dark/sort_asc.svg + res/icons/icons_dark/table_view.svg + res/icons/icons_dark/vote.svg res/icons/tx_pos_ss.svg res/icons/tx_por.svg @@ -180,6 +200,7 @@ res/icons/light_mode.svg res/icons/light_mode_active.svg res/icons/no_result.svg + res/icons/open_link.svg diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 528e9d6c8e..f2471c43b6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -20,7 +20,7 @@ #include "signverifymessagedialog.h" #include "optionsdialog.h" #include "aboutdialog.h" -#include "votingdialog.h" +#include "voting/votingpage.h" #include "clientmodel.h" #include "walletmodel.h" #include "researcher/researchermodel.h" @@ -48,6 +48,7 @@ #endif #include +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -186,7 +188,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): receiveCoinsPage = new ReceiveCoinsPage(this); transactionView = new TransactionView(this); addressBookPage = new FavoritesPage(this); - votingPage = new VotingDialog(this); + votingPage = new VotingPage(this); signVerifyMessageDialog = new SignVerifyMessageDialog(this); @@ -750,6 +752,7 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) rpcConsole->setClientModel(clientModel); addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); + votingPage->setOptionsModel(clientModel->getOptionsModel()); } } @@ -772,7 +775,6 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) addressBookPage->setAddressTableModel(walletModel->getAddressTableModel()); receiveCoinsPage->setAddressTableModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); - votingPage->setModel(walletModel); signVerifyMessageDialog->setModel(walletModel); setEncryptionStatus(walletModel->getEncryptionStatus()); @@ -802,6 +804,11 @@ void BitcoinGUI::setResearcherModel(ResearcherModel *researcherModel) connect(researcherModel, SIGNAL(beaconChanged()), this, SLOT(updateBeaconIcon())); } +void BitcoinGUI::setVotingModel(VotingModel *votingModel) +{ + votingPage->setVotingModel(votingModel); +} + void BitcoinGUI::createTrayIcon() { #ifndef Q_OS_MAC diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index f7520e0692..7ef1163cc8 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -16,12 +16,13 @@ class TransactionTableModel; class ClientModel; class WalletModel; class ResearcherModel; +class VotingModel; class TransactionView; class OverviewPage; class FavoritesPage; class ReceiveCoinsPage; class SendCoinsDialog; -class VotingDialog; +class VotingPage; class SignVerifyMessageDialog; class Notificator; class RPCConsole; @@ -65,6 +66,11 @@ class BitcoinGUI : public QMainWindow */ void setResearcherModel(ResearcherModel *researcherModel); + /** Set the voting model. + The voting model facilitates presentation of and interaction with network polls and votes. + */ + void setVotingModel(VotingModel *votingModel); + protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); @@ -83,7 +89,7 @@ class BitcoinGUI : public QMainWindow ReceiveCoinsPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; TransactionView *transactionView; - VotingDialog *votingPage; + VotingPage *votingPage; SignVerifyMessageDialog *signVerifyMessageDialog; std::unique_ptr updateMessageDialog; diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 0e9380de30..fd5acfad22 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -923,7 +923,11 @@ - + + + true + + diff --git a/src/qt/forms/voting/pollcard.ui b/src/qt/forms/voting/pollcard.ui new file mode 100644 index 0000000000..894c6925c3 --- /dev/null +++ b/src/qt/forms/voting/pollcard.ui @@ -0,0 +1,235 @@ + + + PollCard + + + + 0 + 0 + 666 + 146 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 16 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Title + + + Qt::PlainText + + + true + + + + + + + 3 + + + + + Votes: + + + + + + + + + + + + + + + + + + + + + Expiration: + + + + + + + Top Answer: + + + + + + + + + + Qt::PlainText + + + true + + + + + + + Total Weight: + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + true + + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Balance + + + + + + + Magnitude + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Voting finished. + + + + + + + Vote + + + + + + + Details + + + + + + + + + + + diff --git a/src/qt/forms/voting/pollcardview.ui b/src/qt/forms/voting/pollcardview.ui new file mode 100644 index 0000000000..3f931a54c9 --- /dev/null +++ b/src/qt/forms/voting/pollcardview.ui @@ -0,0 +1,167 @@ + + + PollCardView + + + + 0 + 0 + 697 + 228 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + 0 + 0 + 695 + 226 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 12 + + + 12 + + + 12 + + + 12 + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + 1 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 12 + + + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + diff --git a/src/qt/forms/voting/polldetails.ui b/src/qt/forms/voting/polldetails.ui new file mode 100644 index 0000000000..9bdae8e4b3 --- /dev/null +++ b/src/qt/forms/voting/polldetails.ui @@ -0,0 +1,137 @@ + + + PollDetails + + + + 0 + 0 + 599 + 103 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Date Range + + + Qt::TextSelectableByMouse + + + + + + + Title + + + Qt::PlainText + + + true + + + Qt::TextSelectableByMouse + + + + + + + Question + + + Qt::PlainText + + + true + + + Qt::TextSelectableByMouse + + + + + + + + + + + + + 1 + 0 + + + + URL + + + Qt::RichText + + + true + + + Qt::TextBrowserInteraction + + + + + + + + + 0 + + + + + Top Answer: + + + + + + + + 1 + 0 + + + + Qt::PlainText + + + true + + + Qt::TextSelectableByMouse + + + + + + + + + + diff --git a/src/qt/forms/voting/pollresultchoiceitem.ui b/src/qt/forms/voting/pollresultchoiceitem.ui new file mode 100644 index 0000000000..b513af6856 --- /dev/null +++ b/src/qt/forms/voting/pollresultchoiceitem.ui @@ -0,0 +1,106 @@ + + + PollResultChoiceItem + + + + 0 + 0 + 666 + 90 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Choice Text + + + Qt::PlainText + + + true + + + Qt::TextSelectableByMouse + + + + + + + 1000 + + + false + + + false + + + + + + + + + Weight: + + + + + + + 123 + + + Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 1% + + + Qt::TextSelectableByMouse + + + + + + + + + + diff --git a/src/qt/forms/voting/pollresultdialog.ui b/src/qt/forms/voting/pollresultdialog.ui new file mode 100644 index 0000000000..c3fa87134c --- /dev/null +++ b/src/qt/forms/voting/pollresultdialog.ui @@ -0,0 +1,157 @@ + + + PollResultDialog + + + + 0 + 0 + 732 + 524 + + + + Poll Details + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + + + + + true + + + + + 0 + 0 + 710 + 430 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 12 + + + 9 + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + Poll ID + + + ID + + + Qt::TextSelectableByMouse + + + + + + + QDialogButtonBox::Close + + + + + + + + + + PollDetails + QWidget +
voting/polldetails.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/voting/polltab.ui b/src/qt/forms/voting/polltab.ui new file mode 100644 index 0000000000..130bbf251b --- /dev/null +++ b/src/qt/forms/voting/polltab.ui @@ -0,0 +1,135 @@ + + + PollTab + + + + 0 + 0 + 666 + 424 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 12 + + + 12 + + + 12 + + + 12 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::CustomContextMenu + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + false + + + true + + + false + + + true + + + false + + + + + + + + + + + + + + + PollCardView + QWidget +
voting/pollcardview.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/voting/votingpage.ui b/src/qt/forms/voting/votingpage.ui new file mode 100644 index 0000000000..b056d81d37 --- /dev/null +++ b/src/qt/forms/voting/votingpage.ui @@ -0,0 +1,235 @@ + + + VotingPage + + + + 0 + 0 + 899 + 456 + + + + Voting + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Polls + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Search by title + + + true + + + + + + + + + + + 9 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + View as list. + + + Alt+T + + + + + + + View as table. + + + Alt+T + + + + + + + Sort by... + + + Alt+S + + + QToolButton::InstantPopup + + + + + + + &Refresh + + + + + + + Create &Poll + + + + + + + + + + A new poll is available. Press "Refresh" to load it. + + + Qt::AlignCenter + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + &Active + + + + + &Completed + + + + + + + + + + + + PollTab + QWidget +
voting/polltab.h
+ 1 +
+
+ + +
diff --git a/src/qt/noresult.cpp b/src/qt/noresult.cpp index 0eddffd745..44aac0888b 100644 --- a/src/qt/noresult.cpp +++ b/src/qt/noresult.cpp @@ -45,3 +45,18 @@ void NoResult::setContentWidget(QWidget* widget) ui->verticalLayout->insertWidget(index, widget, 0, Qt::AlignHCenter); } } + +void NoResult::showDefaultNothingHereTitle() +{ + setTitle(tr("Nothing here yet...")); +} + +void NoResult::showDefaultNoResultTitle() +{ + setTitle(tr("No results available.")); +} + +void NoResult::showDefaultLoadingTitle() +{ + setTitle(tr("Loading...")); +} diff --git a/src/qt/noresult.h b/src/qt/noresult.h index 3be380156c..523a6f5a66 100644 --- a/src/qt/noresult.h +++ b/src/qt/noresult.h @@ -21,9 +21,19 @@ class NoResult : public QWidget QWidget* contentWidget(); + template + ContentWidget* contentWidgetAs() + { + return qobject_cast(m_content_widget); + } + + public slots: void setTitle(const QString& title); void setContentWidget(QWidget* widget); + void showDefaultNothingHereTitle(); + void showDefaultNoResultTitle(); + void showDefaultLoadingTitle(); private: Ui::NoResult *ui; diff --git a/src/qt/res/icons/icons_dark/add.svg b/src/qt/res/icons/icons_dark/add.svg new file mode 100644 index 0000000000..6e0ddaf489 --- /dev/null +++ b/src/qt/res/icons/icons_dark/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/create.svg b/src/qt/res/icons/icons_dark/create.svg new file mode 100644 index 0000000000..082f89a4fc --- /dev/null +++ b/src/qt/res/icons/icons_dark/create.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/edit.svg b/src/qt/res/icons/icons_dark/edit.svg new file mode 100644 index 0000000000..dd6416c3ac --- /dev/null +++ b/src/qt/res/icons/icons_dark/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/hamburger.svg b/src/qt/res/icons/icons_dark/hamburger.svg new file mode 100644 index 0000000000..4e8bbcfbfe --- /dev/null +++ b/src/qt/res/icons/icons_dark/hamburger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/list_view.svg b/src/qt/res/icons/icons_dark/list_view.svg new file mode 100644 index 0000000000..97ac228b9d --- /dev/null +++ b/src/qt/res/icons/icons_dark/list_view.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/qt/res/icons/icons_dark/refresh.svg b/src/qt/res/icons/icons_dark/refresh.svg new file mode 100644 index 0000000000..b7e74dbb12 --- /dev/null +++ b/src/qt/res/icons/icons_dark/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/remove.svg b/src/qt/res/icons/icons_dark/remove.svg new file mode 100644 index 0000000000..7095293dad --- /dev/null +++ b/src/qt/res/icons/icons_dark/remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_dark/sort_asc.svg b/src/qt/res/icons/icons_dark/sort_asc.svg new file mode 100644 index 0000000000..955dbc39d0 --- /dev/null +++ b/src/qt/res/icons/icons_dark/sort_asc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/qt/res/icons/icons_dark/table_view.svg b/src/qt/res/icons/icons_dark/table_view.svg new file mode 100644 index 0000000000..9dcb8e7d5c --- /dev/null +++ b/src/qt/res/icons/icons_dark/table_view.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_dark/vote.svg b/src/qt/res/icons/icons_dark/vote.svg new file mode 100644 index 0000000000..540180830c --- /dev/null +++ b/src/qt/res/icons/icons_dark/vote.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/add.svg b/src/qt/res/icons/icons_light/add.svg new file mode 100644 index 0000000000..1e793cb174 --- /dev/null +++ b/src/qt/res/icons/icons_light/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/create.svg b/src/qt/res/icons/icons_light/create.svg new file mode 100644 index 0000000000..4636571101 --- /dev/null +++ b/src/qt/res/icons/icons_light/create.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/edit.svg b/src/qt/res/icons/icons_light/edit.svg new file mode 100644 index 0000000000..8b954dd05f --- /dev/null +++ b/src/qt/res/icons/icons_light/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/hamburger.svg b/src/qt/res/icons/icons_light/hamburger.svg new file mode 100644 index 0000000000..c23ab15767 --- /dev/null +++ b/src/qt/res/icons/icons_light/hamburger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/list_view.svg b/src/qt/res/icons/icons_light/list_view.svg new file mode 100644 index 0000000000..824625d3d3 --- /dev/null +++ b/src/qt/res/icons/icons_light/list_view.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/qt/res/icons/icons_light/refresh.svg b/src/qt/res/icons/icons_light/refresh.svg new file mode 100644 index 0000000000..d972827332 --- /dev/null +++ b/src/qt/res/icons/icons_light/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/remove.svg b/src/qt/res/icons/icons_light/remove.svg new file mode 100644 index 0000000000..a32a0ed642 --- /dev/null +++ b/src/qt/res/icons/icons_light/remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/icons_light/sort_asc.svg b/src/qt/res/icons/icons_light/sort_asc.svg new file mode 100644 index 0000000000..2e44e5c6fa --- /dev/null +++ b/src/qt/res/icons/icons_light/sort_asc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/qt/res/icons/icons_light/table_view.svg b/src/qt/res/icons/icons_light/table_view.svg new file mode 100644 index 0000000000..677a67d94d --- /dev/null +++ b/src/qt/res/icons/icons_light/table_view.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qt/res/icons/icons_light/vote.svg b/src/qt/res/icons/icons_light/vote.svg new file mode 100644 index 0000000000..94fed95529 --- /dev/null +++ b/src/qt/res/icons/icons_light/vote.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/icons/open_link.svg b/src/qt/res/icons/open_link.svg new file mode 100644 index 0000000000..78bb7afb86 --- /dev/null +++ b/src/qt/res/icons/open_link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index a49411c7db..cf00407ff8 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -44,7 +44,8 @@ QTabWidget::pane, padding: 0.75em 1em; } -AddressBookPage #wrapperFrame { +AddressBookPage #wrapperFrame, +PollCard #detailsFrame { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } @@ -465,6 +466,14 @@ QTabBar::tab:!selected:hover { border-bottom-color: rgb(75, 143, 226); } +#linkIconLabel { + image: url(:/icons/open_link); + min-width: 1em; + max-width: 1em; + min-height: 1em; + max-height: 1em; +} + /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; @@ -823,12 +832,103 @@ TransactionView #filterFrame { /* Voting page */ +VotingPage #tabWrapperWidget { + background: rgba(0, 0, 0, 0.1); +} + +VotingPage QTabWidget::tab-bar { + left: 0; +} + +VotingPage QTabBar::tab { + margin-bottom: 0; + margin: 0 0.75em; +} + +VotingPage QTabBar::tab:first { + margin-left: 1em; +} + +VotingPage QTabWidget::pane { + background-color: rgb(26, 38, 50); + border-top: 0.065em solid rgba(0, 0, 0, 0.3); + border-radius: 0; + padding: 0; +} + +VotingPage QStackedWidget > QWidget { + padding: 0.75em 1em; +} + +VotingPage #tabButtonFrame { + background: transparent; + padding: 0.4em 1em; +} + +VotingPage #cardsToggleButton, +VotingPage #sortButton, +VotingPage #tableToggleButton { + background: none; + border: none; + padding: 0.1em; + width: 1em; + height: 1em; +} + +VotingPage #cardsToggleButton:hover, +VotingPage #cardsToggleButton:focus, +VotingPage #sortButton:hover, +VotingPage #sortButton:focus, +VotingPage #tableToggleButton:hover, +VotingPage #tableToggleButton:focus { + background-color: rgb(21, 126, 205); +} + +VotingPage QProgressBar { + background: none; + border: none; + min-height: 0.25em; + max-height: 0.25em; + padding: 0; +} + +VotingPage QProgressBar::chunk { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgb(26, 145, 235), stop: 1 rgb(160, 150, 255)); + border: none; + border-radius: 0.125em; + padding: 0; +} + +VotingPage #pollReceivedLabel { + background: rgb(25, 90, 240); + padding: 0.25em 1em; + color: white; +} + +PollCard #titleLabel, +PollDetails #titleLabel, PollWizard #pageTitleLabel, -PollWizardTypePage #typeTextLabel { +PollWizardTypePage #typeTextLabel, +VoteWizard #pageTitleLabel { font-weight: bold; color: white; } +PollCard #balanceLabel, +PollCard #magnitudeLabel { + border: 0.065em solid rgb(115, 131, 161); + border-radius: 0.65em; + padding: 0.1em 0.3em; + color: rgb(115, 131, 161); +} + +PollCard #remainingLabel, +PollResultChoiceItem #percentageLabel, +PollResultChoiceItem #weightLabel, +PollResultChoiceItem #weightTextLabel { + color: rgba(204, 208, 209, 0.6); +} + PollResultChoiceItem QProgressBar { background: rgba(0, 0, 0, 0.2); border: none; @@ -838,6 +938,13 @@ PollResultChoiceItem QProgressBar { padding: 0; } +PollDetails #dateRangeLabel, +PollResultDialog #idLabel, +PollWizardSummaryPage #pollIdLabel, +VoteWizardSummaryPage #voteIdLabel { + color: rgba(255, 255, 255, 0.4); +} + PollWizardDetailsPage #pollTypeAlert { background: rgba(255, 240, 0, 0.4); border-radius: 0.25em; @@ -845,7 +952,8 @@ PollWizardDetailsPage #pollTypeAlert { color: white; } -PollWizardDetailsPage #errorLabel { +PollWizardDetailsPage #errorLabel, +VoteWizardBallotPage #errorLabel { background: rgba(255, 0, 0, 0.4); border-radius: 0.25em; padding: 0.1em 0.2em; diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 1b7774cbbd..270ce2387b 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -40,7 +40,8 @@ QTabWidget::pane, padding: 0.75em 1em; } -AddressBookPage #wrapperFrame { +AddressBookPage #wrapperFrame, +PollCard #detailsFrame { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } @@ -456,6 +457,14 @@ QTabBar::tab:!selected:hover { border-bottom-color: rgb(113, 121, 140); } +#linkIconLabel { + image: url(:/icons/open_link); + min-width: 1em; + max-width: 1em; + min-height: 1em; + max-height: 1em; +} + /* RPC Console */ #messagesWidget, #lineEdit, #scraper_log { font-family: "Inconsolata"; @@ -803,12 +812,114 @@ TransactionView #filterFrame { /* Voting page */ +VotingPage #tabWrapperWidget { + background: rgb(244, 247, 249); +} + +VotingPage QTabWidget::tab-bar { + left: 0; +} + +VotingPage QTabBar::tab { + margin-bottom: 0; + margin: 0 0.75em; +} + +VotingPage QTabBar::tab:first { + margin-left: 1em; +} + +VotingPage QTabWidget::pane { + background-color: rgb(235, 236, 238); + border-top: 0.065em solid rgba(0, 0, 0, 0.1); + border-radius: 0; + padding: 0; +} + +VotingPage #tabButtonFrame { + background: transparent; + padding: 0.4em 1em; +} + +VotingPage #cardsToggleButton, +VotingPage #sortButton, +VotingPage #tableToggleButton { + background: none; + border: none; + padding: 0.1em; + width: 1em; + height: 1em; +} + +VotingPage #cardsToggleButton:hover, +VotingPage #cardsToggleButton:focus, +VotingPage #sortButton:hover, +VotingPage #sortButton:focus, +VotingPage #tableToggleButton:hover, +VotingPage #tableToggleButton:focus { + background: rgba(0, 0, 0, 0.1); +} + +VotingPage QProgressBar { + background: none; + border: none; + min-height: 0.25em; + max-height: 0.25em; + padding: 0; +} + +VotingPage QProgressBar::chunk { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgb(100, 20, 255), stop: 1 rgb(180, 40, 255)); + border: none; + border-radius: 0.125em; + padding: 0; +} + +VotingPage #pollReceivedLabel { + background: rgb(75, 30, 250); + padding: 0.25em 1em; + color: white; +} + +PollCard #titleLabel, +PollDetails #titleLabel, PollWizard #pageTitleLabel, PollWizardTypePage #typeTextLabel { color: rgb(43, 52, 69); font-weight: bold; } +PollCard #balanceLabel, +PollCard #magnitudeLabel { + border: 0.065em solid rgb(115, 131, 161); + border-radius: 0.65em; + padding: 0.1em 0.3em; + color: rgb(115, 131, 161); +} + +PollCard #remainingLabel, +PollResultChoiceItem #percentageLabel, +PollResultChoiceItem #weightLabel, +PollResultChoiceItem #weightTextLabel { + color: rgba(58, 70, 93, 0.6); +} + +PollResultChoiceItem QProgressBar { + background: rgba(0, 0, 0, 0.1); + border: none; + border-radius: 0.25em; + min-height: 0.5em; + max-height: 0.5em; + padding: 0; +} + +PollDetails #dateRangeLabel, +PollResultDialog #idLabel, +PollWizardSummaryPage #pollIdLabel, +VoteWizardSummaryPage #voteIdLabel { + color: rgba(0, 0, 0, 0.4); +} + PollWizardDetailsPage #pollTypeAlert { background: rgba(255, 240, 0, 0.6); border-radius: 0.25em; @@ -816,7 +927,8 @@ PollWizardDetailsPage #pollTypeAlert { color: black; } -PollWizardDetailsPage #errorLabel { +PollWizardDetailsPage #errorLabel, +VoteWizardBallotPage #errorLabel { background: rgba(255, 0, 0, 0.4); border-radius: 0.25em; padding: 0.1em 0.2em; diff --git a/src/qt/voting/pollcard.cpp b/src/qt/voting/pollcard.cpp new file mode 100644 index 0000000000..e4776aa908 --- /dev/null +++ b/src/qt/voting/pollcard.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/forms/voting/ui_pollcard.h" +#include "qt/guiutil.h" +#include "qt/voting/pollcard.h" +#include "qt/voting/votingmodel.h" + +// ----------------------------------------------------------------------------- +// Class: PollCard +// ----------------------------------------------------------------------------- + +PollCard::PollCard(const PollItem& poll_item, QWidget* parent) + : QWidget(parent) + , ui(new Ui::PollCard) + , m_expiration(poll_item.m_expiration) +{ + ui->setupUi(this); + + ui->titleLabel->setText(poll_item.m_title); + ui->expirationLabel->setText(GUIUtil::dateTimeStr(poll_item.m_expiration)); + ui->voteCountLabel->setText(QString::number(poll_item.m_total_votes)); + ui->totalWeightLabel->setText(QString::number(poll_item.m_total_weight)); + ui->topAnswerLabel->setText(poll_item.m_top_answer); + + if (!poll_item.m_finished) { + connect(ui->voteButton, &QPushButton::clicked, this, &PollCard::voteRequested); + } else { + delete ui->voteButton; + ui->voteButton = nullptr; + } + + connect(ui->detailsButton, &QPushButton::clicked, this, &PollCard::detailsRequested); +} + +PollCard::~PollCard() +{ + delete ui; +} + +void PollCard::updateRemainingTime(const QDateTime& now) +{ + if (ui->voteButton == nullptr) { + return; + } + + if (m_expiration < now) { + delete ui->voteButton; + ui->voteButton = nullptr; + ui->remainingLabel->setText(tr("Voting finished.")); + + return; + } + + constexpr int64_t three_days = 60 * 60 * 24 * 3; + const int64_t remaining_secs = now.secsTo(m_expiration); + + ui->remainingLabel->setText(QObject::tr("%1 remaining.") + .arg(GUIUtil::formatNiceTimeOffset(remaining_secs))); + + if (remaining_secs < three_days) { + ui->remainingLabel->setStyleSheet(QStringLiteral("color: red")); + } +} + +void PollCard::updateIcons(const QString& theme) +{ + if (ui->voteButton != nullptr) { + ui->voteButton->setIcon(QIcon(":/icons/" + theme + "_vote")); + } + + ui->detailsButton->setIcon(QIcon(":/icons/" + theme + "_hamburger")); +} diff --git a/src/qt/voting/pollcard.h b/src/qt/voting/pollcard.h new file mode 100644 index 0000000000..2cc8b2bbba --- /dev/null +++ b/src/qt/voting/pollcard.h @@ -0,0 +1,38 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLCARD_H +#define VOTING_POLLCARD_H + +#include +#include + +namespace Ui { +class PollCard; +} + +class PollItem; + +class PollCard : public QWidget +{ + Q_OBJECT + +public: + explicit PollCard(const PollItem& poll_item, QWidget* parent = nullptr); + ~PollCard(); + +signals: + void voteRequested(); + void detailsRequested(); + +public slots: + void updateRemainingTime(const QDateTime& now); + void updateIcons(const QString& theme); + +private: + Ui::PollCard* ui; + QDateTime m_expiration; +}; + +#endif // VOTING_POLLCARD_H diff --git a/src/qt/voting/pollcardview.cpp b/src/qt/voting/pollcardview.cpp new file mode 100644 index 0000000000..d6bc7a62b7 --- /dev/null +++ b/src/qt/voting/pollcardview.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/forms/voting/ui_pollcardview.h" +#include "qt/voting/pollcard.h" +#include "qt/voting/pollcardview.h" +#include "qt/voting/polltablemodel.h" +#include "qt/voting/votingmodel.h" + +#include +#include + +namespace { +constexpr int REFRESH_TIMER_INTERVAL_MSECS = 60 * 1000; +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: PollCardView +// ----------------------------------------------------------------------------- + +PollCardView::PollCardView(QWidget* parent) + : QWidget(parent) + , ui(new Ui::PollCardView) +{ + ui->setupUi(this); +} + +PollCardView::~PollCardView() +{ + delete ui; +} + +void PollCardView::setModel(PollTableModel* model) +{ + m_model = model; + + if (!model) { + return; + } + + connect(model, &PollTableModel::layoutChanged, this, &PollCardView::redraw); + + if (!m_refresh_timer && m_model->includesActivePolls()) { + m_refresh_timer.reset(new QTimer(this)); + m_refresh_timer->setTimerType(Qt::VeryCoarseTimer); + + connect( + m_refresh_timer.get(), &QTimer::timeout, + this, &PollCardView::updateRemainingTime); + } +} + +void PollCardView::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + + if (m_refresh_timer) { + updateRemainingTime(); + m_refresh_timer->start(REFRESH_TIMER_INTERVAL_MSECS); + } +} + +void PollCardView::hideEvent(QHideEvent* event) +{ + QWidget::hideEvent(event); + + if (m_refresh_timer) { + m_refresh_timer->stop(); + } +} + +void PollCardView::redraw() +{ + // TODO: destroying and re-creating the widgets is not very efficient for + // sorting and filtering. Hook up model events for these operations. + clear(); + + if (!m_model) { + return; + } + + const QDateTime now = QDateTime::currentDateTimeUtc(); + const QModelIndex dummy_parent; + + for (int i = 0; i < m_model->rowCount(dummy_parent); ++i) { + if (const PollItem* poll_item = m_model->rowItem(i)) { + PollCard* card = new PollCard(*poll_item, this); + card->updateRemainingTime(now); + card->updateIcons(m_theme); + + ui->cardsLayout->addWidget(card); + + if (!poll_item->m_finished) { + connect(card, &PollCard::voteRequested, [this, i]() { + emit voteRequested(i); + }); + } + + connect(card, &PollCard::detailsRequested, [this, i]() { + emit detailsRequested(i); + }); + } + } +} + +void PollCardView::clear() +{ + while (ui->cardsLayout->count() > 0) { + delete ui->cardsLayout->takeAt(0)->widget(); + } + + ui->scrollArea->verticalScrollBar()->setValue(0); +} + +void PollCardView::updateRemainingTime() +{ + if (ui->cardsLayout->count() == 0) { + return; + } + + const QDateTime now = QDateTime::currentDateTimeUtc(); + + for (int i = 0; i < ui->cardsLayout->count(); ++i) { + QLayoutItem* item = ui->cardsLayout->itemAt(i); + + if (auto* card = qobject_cast(item->widget())) { + card->updateRemainingTime(now); + } + } +} + +void PollCardView::updateIcons(const QString& theme) +{ + m_theme = theme; + + for (int i = 0; i < ui->cardsLayout->count(); ++i) { + QLayoutItem* item = ui->cardsLayout->itemAt(i); + + if (auto* card = qobject_cast(item->widget())) { + card->updateIcons(theme); + } + } +} diff --git a/src/qt/voting/pollcardview.h b/src/qt/voting/pollcardview.h new file mode 100644 index 0000000000..9b30920f99 --- /dev/null +++ b/src/qt/voting/pollcardview.h @@ -0,0 +1,55 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLCARDVIEW_H +#define VOTING_POLLCARDVIEW_H + +#include +#include + +namespace Ui { +class PollCardView; +} + +class PollTableModel; + +QT_BEGIN_NAMESPACE +class QHideEvent; +class QShowEvent; +class QTimer; +QT_END_NAMESPACE + +class PollCardView : public QWidget +{ + Q_OBJECT + +public: + explicit PollCardView(QWidget* parent = nullptr); + ~PollCardView(); + + void setModel(PollTableModel* model); + + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; + +signals: + void voteRequested(int row); + void detailsRequested(int row); + +public slots: + void updateRemainingTime(); + void updateIcons(const QString& theme); + +private: + Ui::PollCardView* ui; + PollTableModel* m_model; + std::unique_ptr m_refresh_timer; + QString m_theme; + +private slots: + void redraw(); + void clear(); +}; + +#endif // VOTING_POLLCARDVIEW_H diff --git a/src/qt/voting/polldetails.cpp b/src/qt/voting/polldetails.cpp new file mode 100644 index 0000000000..8ea397ba63 --- /dev/null +++ b/src/qt/voting/polldetails.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/guiutil.h" +#include "qt/forms/voting/ui_polldetails.h" +#include "qt/voting/polldetails.h" +#include "qt/voting/votingmodel.h" + +PollDetails::PollDetails(QWidget* parent) + : QWidget(parent) + , ui(new Ui::PollDetails) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->dateRangeLabel, 9); + GRC::ScaleFontPointSize(ui->titleLabel, 12); + GRC::ScaleFontPointSize(ui->questionLabel, 11); +} + +PollDetails::~PollDetails() +{ + delete ui; +} + +void PollDetails::setItem(const PollItem& poll_item) +{ + ui->dateRangeLabel->setText(QStringLiteral("%1 → %2") + .arg(GUIUtil::dateTimeStr(poll_item.m_start_time)) + .arg(GUIUtil::dateTimeStr(poll_item.m_expiration))); + + ui->titleLabel->setText(poll_item.m_title); + ui->urlLabel->setText(QStringLiteral("%1").arg(poll_item.m_url)); + + ui->questionLabel->setVisible(!poll_item.m_question.isEmpty()); + ui->questionLabel->setText(poll_item.m_question); + + ui->topAnswerTextLabel->setVisible(!poll_item.m_top_answer.isEmpty()); + ui->topAnswerLabel->setVisible(!poll_item.m_top_answer.isEmpty()); + ui->topAnswerLabel->setText(poll_item.m_top_answer); +} diff --git a/src/qt/voting/polldetails.h b/src/qt/voting/polldetails.h new file mode 100644 index 0000000000..5eba695768 --- /dev/null +++ b/src/qt/voting/polldetails.h @@ -0,0 +1,30 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLDETAILS_H +#define VOTING_POLLDETAILS_H + +#include + +namespace Ui { +class PollDetails; +} + +class PollItem; + +class PollDetails : public QWidget +{ + Q_OBJECT + +public: + explicit PollDetails(QWidget* parent = nullptr); + ~PollDetails(); + + void setItem(const PollItem& poll_item); + +private: + Ui::PollDetails* ui; +}; + +#endif // VOTING_POLLDETAILS_H diff --git a/src/qt/voting/pollresultchoiceitem.cpp b/src/qt/voting/pollresultchoiceitem.cpp new file mode 100644 index 0000000000..eb32889761 --- /dev/null +++ b/src/qt/voting/pollresultchoiceitem.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/forms/voting/ui_pollresultchoiceitem.h" +#include "qt/voting/pollresultchoiceitem.h" +#include "qt/voting/votingmodel.h" + +namespace { +constexpr char CHUNK_STYLE_TEMPLATE[] = + "QProgressBar::chunk {" + "border-radius: 0.25em;" + "background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 %1, stop: 1 %2);" + "}"; + +const QColor BAR_START_COLOR(0, 219, 222); +const QColor BAR_END_COLOR(252, 0, 255); + +QString CalculateBarStyle(const double ratio) +{ + const QColor end_color( + (1 - ratio) * BAR_START_COLOR.red() + ratio * BAR_END_COLOR.red(), + (1 - ratio) * BAR_START_COLOR.green() + ratio * BAR_END_COLOR.green(), + (1 - ratio) * BAR_START_COLOR.blue() + ratio * BAR_END_COLOR.blue()); + + return QString(CHUNK_STYLE_TEMPLATE) + .arg(BAR_START_COLOR.name()) + .arg(end_color.name()); +} +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: PollResultChoiceItem +// ----------------------------------------------------------------------------- + +PollResultChoiceItem::PollResultChoiceItem( + const VoteResultItem& choice, + const double total_poll_weight, + const double top_choice_weight, + QWidget* parent) + : QFrame(parent) + , ui(new Ui::PollResultChoiceItem) +{ + ui->setupUi(this); + + ui->choiceLabel->setText(choice.m_label); + ui->weightLabel->setText(QString::number(choice.m_weight)); + + // If the poll has no responses yet, skip the rest of the ratio-dependent + // calculations and formatting: + // + if (total_poll_weight == 0) { + ui->percentageLabel->hide(); + return; + } + + const double relative_ratio = choice.m_weight / top_choice_weight; + const double percentage = 100.0 * choice.m_weight / total_poll_weight; + + ui->percentageLabel->setText(QStringLiteral("%1%").arg(percentage, 0, 'f', 2)); + + // Limit the lower bound to the smallest degree that Qt will draw the + // progress bar's rounded border at for the default dialog size: + // + if (percentage < 0.5) { + ui->weightBar->setValue(0); + } else { + ui->weightBar->setValue(std::max(12.0, 1000.0 * relative_ratio)); + } + + ui->weightBar->setStyleSheet(CalculateBarStyle(relative_ratio)); +} + +PollResultChoiceItem::~PollResultChoiceItem() +{ + delete ui; +} diff --git a/src/qt/voting/pollresultchoiceitem.h b/src/qt/voting/pollresultchoiceitem.h new file mode 100644 index 0000000000..9d2c51c8c5 --- /dev/null +++ b/src/qt/voting/pollresultchoiceitem.h @@ -0,0 +1,32 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLRESULTCHOICEITEM_H +#define VOTING_POLLRESULTCHOICEITEM_H + +#include + +namespace Ui { +class PollResultChoiceItem; +} + +class VoteResultItem; + +class PollResultChoiceItem : public QFrame +{ + Q_OBJECT + +public: + explicit PollResultChoiceItem( + const VoteResultItem& choice, + const double total_poll_weight, + const double top_choice_weight, + QWidget* parent = nullptr); + ~PollResultChoiceItem(); + +private: + Ui::PollResultChoiceItem* ui; +}; + +#endif // VOTING_POLLRESULTCHOICEITEM_H diff --git a/src/qt/voting/pollresultdialog.cpp b/src/qt/voting/pollresultdialog.cpp new file mode 100644 index 0000000000..c73f1b4126 --- /dev/null +++ b/src/qt/voting/pollresultdialog.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_pollresultdialog.h" +#include "qt/voting/pollresultchoiceitem.h" +#include "qt/voting/pollresultdialog.h" +#include "qt/voting/votingmodel.h" + +#include + +namespace { +std::vector +SortResultsByWeight(const std::vector& choices) +{ + std::vector sorted; + + for (const auto& choice_result : choices) { + sorted.emplace_back(&choice_result); + } + + constexpr auto descending = [](const VoteResultItem* a, const VoteResultItem* b) { + return *b < *a; + }; + + std::stable_sort(sorted.begin(), sorted.end(), descending); + + return sorted; +} +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: PollResultChoiceItem +// ----------------------------------------------------------------------------- + +PollResultDialog::PollResultDialog(const PollItem& poll_item, QWidget* parent) + : QDialog(parent) + , ui(new Ui::PollResultDialog) +{ + ui->setupUi(this); + + setModal(true); + setAttribute(Qt::WA_DeleteOnClose, true); + resize(GRC::ScaleSize(this, 740, 580)); + + GRC::ScaleFontPointSize(ui->idLabel, 8); + + ui->details->setItem(poll_item); + ui->idLabel->setText(poll_item.m_id); + + const auto sorted = SortResultsByWeight(poll_item.m_choices); + + ui->choicesLayout->addWidget(new PollResultChoiceItem( + *sorted.front(), + poll_item.m_total_weight, + sorted.front()->m_weight)); + + for (size_t i = 1; i < poll_item.m_choices.size(); ++i) { + QFrame* line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + ui->choicesLayout->addWidget(line); + + ui->choicesLayout->addWidget(new PollResultChoiceItem( + *sorted[i], + poll_item.m_total_weight, + sorted.front()->m_weight)); + } + + ui->buttonBox->button(QDialogButtonBox::Close)->setIcon(QIcon()); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +PollResultDialog::~PollResultDialog() +{ + delete ui; +} diff --git a/src/qt/voting/pollresultdialog.h b/src/qt/voting/pollresultdialog.h new file mode 100644 index 0000000000..583b2e5f0d --- /dev/null +++ b/src/qt/voting/pollresultdialog.h @@ -0,0 +1,28 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLRESULTDIALOG_H +#define VOTING_POLLRESULTDIALOG_H + +#include + +namespace Ui { +class PollResultDialog; +} + +class PollItem; + +class PollResultDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PollResultDialog(const PollItem& poll_item, QWidget* parent = nullptr); + ~PollResultDialog(); + +private: + Ui::PollResultDialog* ui; +}; + +#endif // VOTING_POLLRESULTDIALOG_H diff --git a/src/qt/voting/polltab.cpp b/src/qt/voting/polltab.cpp new file mode 100644 index 0000000000..e612d8dd4e --- /dev/null +++ b/src/qt/voting/polltab.cpp @@ -0,0 +1,292 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/forms/voting/ui_polltab.h" +#include "qt/noresult.h" +#include "qt/voting/pollresultdialog.h" +#include "qt/voting/polltab.h" +#include "qt/voting/polltablemodel.h" +#include "qt/voting/votewizard.h" +#include "qt/voting/votingmodel.h" + +#include +#include +#include +#include +#include +#include + +using namespace GRC; + +namespace { +QString RefreshMessage() +{ + return QCoreApplication::translate("PollTab", "Press \"Refresh\" to update the list."); +} + +QString WaitMessage() +{ + return QCoreApplication::translate("PollTab", "This may take several minutes."); +} + +QString FullRefreshMessage() +{ + return QStringLiteral("%1 %2").arg(RefreshMessage()).arg(WaitMessage()); +} +} // Anonymous namespace + +//! +//! \brief An infinite progress bar that provides a loading animation while +//! refreshing the polls lists. +//! +class LoadingBar : public QProgressBar +{ + Q_OBJECT + + static constexpr int MAX = std::numeric_limits::max(); + +public: + LoadingBar(QWidget* parent = nullptr) + : QProgressBar(parent) + , m_active(false) + { + setRange(0, MAX); + setGraphicsEffect(&m_opacity_effect); + hide(); + + m_fade_anim.setTargetObject(&m_opacity_effect); + m_fade_anim.setPropertyName("opacity"); + m_fade_anim.setDuration(1000); + m_fade_anim.setStartValue(1); + m_fade_anim.setEndValue(0); + m_fade_anim.setEasingCurve(QEasingCurve::OutQuad); + + m_size_anim.setTargetObject(this); + m_size_anim.setPropertyName("value"); + m_size_anim.setDuration(1000); + m_size_anim.setStartValue(0); + m_size_anim.setEndValue(MAX); + m_size_anim.setEasingCurve(QEasingCurve::OutQuad); + + connect(&m_fade_anim, &QPropertyAnimation::finished, this, &QProgressBar::hide); + } + + void start() + { + if (m_active) { + return; + } + + m_active = true; + m_fade_anim.stop(); + m_opacity_effect.setOpacity(1); + + m_size_anim.stop(); + m_size_anim.setLoopCount(-1); // Infinite + m_size_anim.setStartValue(0); + m_size_anim.setEndValue(MAX); + m_size_anim.start(); + + connect( + &m_size_anim, &QAbstractAnimation::currentLoopChanged, + this, &LoadingBar::invertBarAnimation); + + setInvertedAppearance(false); + reset(); + raise(); + show(); + } + + void finish() + { + m_active = false; + m_size_anim.setLoopCount(m_size_anim.currentLoop() + 1); + m_fade_anim.start(); + + disconnect( + &m_size_anim, &QAbstractAnimation::currentLoopChanged, + this, &LoadingBar::invertBarAnimation); + } + +private: + bool m_active; + QGraphicsOpacityEffect m_opacity_effect; + QPropertyAnimation m_fade_anim; + QPropertyAnimation m_size_anim; + +private slots: + void invertBarAnimation() + { + const bool inverted = invertedAppearance(); + + m_size_anim.setStartValue(inverted ? 0 : MAX); + m_size_anim.setEndValue(inverted ? MAX : 0); + setInvertedAppearance(!inverted); + } +}; // LoadingBar + +// ----------------------------------------------------------------------------- +// Class: PollTab +// ----------------------------------------------------------------------------- + +PollTab::PollTab(QWidget* parent) + : QWidget(parent) + , ui(new Ui::PollTab) + , m_model(new PollTableModel(this)) + , m_no_result(new NoResult(this)) + , m_loading(new LoadingBar(this)) +{ + ui->setupUi(this); + + ui->tabLayout->addWidget(m_no_result.get()); + ui->stack->hide(); + + ui->table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->table->sortByColumn(PollTableModel::Expiration, Qt::AscendingOrder); + + m_no_result->setContentWidget(new QLabel(FullRefreshMessage())); + + connect(ui->cards, &PollCardView::voteRequested, this, &PollTab::showVoteRowDialog); + connect(ui->cards, &PollCardView::detailsRequested, this, &PollTab::showDetailsRowDialog); + connect(ui->table, &QAbstractItemView::doubleClicked, this, &PollTab::showPreferredDialog); + connect(ui->table, &QWidget::customContextMenuRequested, this, &PollTab::showTableContextMenu); + connect(m_model.get(), &PollTableModel::layoutChanged, this, &PollTab::finishRefresh); +} + +PollTab::~PollTab() +{ + delete ui; +} + +void PollTab::setVotingModel(VotingModel* model) +{ + m_voting_model = model; + m_model->setModel(model); + + ui->cards->setModel(m_model.get()); + ui->table->setModel(m_model.get()); +} + +void PollTab::setPollFilterFlags(PollFilterFlag flags) +{ + m_model->setPollFilterFlags(flags); +} + +void PollTab::changeViewMode(const ViewId view_id) +{ + ui->stack->setCurrentIndex(view_id); +} + +void PollTab::refresh() +{ + if (m_model->empty()) { + m_no_result->showDefaultLoadingTitle(); + m_no_result->contentWidgetAs()->setText(WaitMessage()); + } + + m_loading->start(); + m_model->refresh(); +} + +void PollTab::filter(const QString& needle) +{ + if (needle != m_last_filter) { + m_model->changeTitleFilter(needle); + m_last_filter = needle; + } +} + +void PollTab::sort(const int column) +{ + const Qt::SortOrder order = m_model->sort(column); + ui->table->horizontalHeader()->setSortIndicator(column, order); +} + +void PollTab::updateIcons(const QString& theme) +{ + ui->cards->updateIcons(theme); +} + +const PollItem* PollTab::selectedTableItem() const +{ + if (!ui->table->selectionModel()->hasSelection()) { + return nullptr; + } + + return m_model->rowItem( + ui->table->selectionModel()->selectedIndexes().first().row()); +} + +void PollTab::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + m_loading->setFixedWidth(event->size().width()); +} + +void PollTab::finishRefresh() +{ + m_loading->finish(); + ui->stack->setVisible(!m_model->empty()); + m_no_result->setVisible(m_model->empty()); + + if (m_model->empty()) { + m_no_result->showDefaultNoResultTitle(); + m_no_result->contentWidgetAs()->setText(FullRefreshMessage()); + } +} + +void PollTab::showVoteRowDialog(int row) +{ + if (const PollItem* const poll_item = m_model->rowItem(row)) { + showVoteDialog(*poll_item); + } +} + +void PollTab::showVoteDialog(const PollItem& poll_item) +{ + (new VoteWizard(poll_item, *m_voting_model, this))->show(); +} + +void PollTab::showDetailsRowDialog(int row) +{ + if (const PollItem* const poll_item = m_model->rowItem(row)) { + showDetailsDialog(*poll_item); + } +} + +void PollTab::showDetailsDialog(const PollItem& poll_item) +{ + (new PollResultDialog(poll_item, this))->show(); +} + +void PollTab::showPreferredDialog(const QModelIndex& index) +{ + if (const PollItem* const poll_item = m_model->rowItem(index.row())) { + if (poll_item->m_finished) { + showDetailsDialog(*poll_item); + } else { + showVoteDialog(*poll_item); + } + } +} + +void PollTab::showTableContextMenu(const QPoint& pos) +{ + if (const PollItem* const poll_item = selectedTableItem()) { + QMenu menu; + menu.addAction(tr("Show Results"), [this, poll_item]() { + showDetailsDialog(*poll_item); + }); + + if (!poll_item->m_finished) { + menu.addAction(tr("Vote"), [this, poll_item]() { + showVoteDialog(*poll_item); + }); + } + + menu.exec(ui->table->viewport()->mapToGlobal(pos)); + } +} + +#include "qt/voting/polltab.moc" diff --git a/src/qt/voting/polltab.h b/src/qt/voting/polltab.h new file mode 100644 index 0000000000..93675ac389 --- /dev/null +++ b/src/qt/voting/polltab.h @@ -0,0 +1,81 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_POLLTAB_H +#define VOTING_POLLTAB_H + +#include "gridcoin/voting/filter.h" + +#include +#include +#include + +namespace Ui { +class PollTab; +} + +class LoadingBar; +class NoResult; +class PollItem; +class PollTableModel; +class VotingModel; + +class PollTab : public QWidget +{ + Q_OBJECT + +public: + //! + //! \brief Represents the offsets of the main tabs on the voting page. + //! + enum TabId + { + TabActive, + TabFinished, + }; + + //! + //! \brief Represents the data views available in the stack for each tab. + //! + enum ViewId + { + ViewCards, + ViewTable, + }; + + explicit PollTab(QWidget* parent = nullptr); + ~PollTab(); + + void setVotingModel(VotingModel* voting_model); + void setPollFilterFlags(GRC::PollFilterFlag flags); + +public slots: + void changeViewMode(const ViewId view_id); + void refresh(); + void filter(const QString& needle); + void sort(const int column); + void updateIcons(const QString& theme); + +private: + Ui::PollTab* ui; + VotingModel* m_voting_model; + std::unique_ptr m_model; + std::unique_ptr m_no_result; + std::unique_ptr m_loading; + QString m_last_filter; + + const PollItem* selectedTableItem() const; + void resizeEvent(QResizeEvent* event) override; + +private slots: + void finishRefresh(); + void showVoteRowDialog(int row); + void showVoteDialog(const PollItem& poll_item); + void showDetailsRowDialog(int row); + void showDetailsDialog(const PollItem& poll_item); + void showPreferredDialog(const QModelIndex& index); + void showTableContextMenu(const QPoint& pos); +}; + +#endif // VOTING_POLLTAB_H diff --git a/src/qt/voting/votingpage.cpp b/src/qt/voting/votingpage.cpp new file mode 100644 index 0000000000..129105eb3c --- /dev/null +++ b/src/qt/voting/votingpage.cpp @@ -0,0 +1,166 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_votingpage.h" +#include "qt/optionsmodel.h" +#include "qt/voting/polltab.h" +#include "qt/voting/polltablemodel.h" +#include "qt/voting/pollwizard.h" +#include "qt/voting/votingmodel.h" +#include "qt/voting/votingpage.h" + +#include +#include +#include + +using namespace GRC; + +namespace { +//! +//! \brief Provides a dropdown used to sort polls, especially for the card view. +//! +class PollSortMenu : public QMenu +{ + Q_OBJECT + +public: + PollSortMenu(VotingPage* parent) : QMenu(parent) + { + const PollTableModel model; + const QModelIndex dummy; + + for (int column = 0; column < model.columnCount(dummy); ++column) { + addAction(model.columnName(column), [parent, column]() { + parent->currentTab().sort(column); + }); + } + } +}; // PollSortMenu +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: VotingPage +// ----------------------------------------------------------------------------- + +VotingPage::VotingPage(QWidget* parent) + : QWidget(parent) + , ui(new Ui::VotingPage) + , m_filter_action(new QAction()) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->headerTitleLabel, 15); + + m_tabs = { + ui->activePollsTab, + ui->finishedPollsTab + }; + + ui->activePollsTab->setPollFilterFlags(PollFilterFlag::ACTIVE); + ui->finishedPollsTab->setPollFilterFlags(PollFilterFlag::FINISHED); + + m_filter_action->setShortcut(Qt::CTRL + Qt::Key_F); + ui->filterLineEdit->addAction(m_filter_action.get(), QLineEdit::LeadingPosition); + ui->cardsToggleButton->hide(); + ui->sortButton->setMenu(new PollSortMenu(this)); + + // Move the buttons from the form into the tab widget's tab bar: + ui->tabWidget->setCornerWidget(ui->tabButtonFrame); + + // Move the notification label from the form into the active polls tab: + qobject_cast(ui->activePollsTab->layout())->insertWidget(0, ui->pollReceivedLabel); + ui->pollReceivedLabel->hide(); + + connect(m_filter_action.get(), &QAction::triggered, [this]() { + ui->filterLineEdit->setFocus(); + ui->filterLineEdit->selectAll(); + }); + connect(ui->filterLineEdit, &QLineEdit::textChanged, [this](const QString& pattern) { + currentTab().filter(pattern); + m_current_filter = pattern; + }); + connect(ui->cardsToggleButton, &QToolButton::clicked, [this]() { + for (auto* tab : m_tabs) { + tab->changeViewMode(PollTab::ViewCards); + } + + ui->cardsToggleButton->hide(); + ui->tableToggleButton->show(); + }); + connect(ui->tableToggleButton, &QToolButton::clicked, [this]() { + for (auto* tab : m_tabs) { + tab->changeViewMode(PollTab::ViewTable); + } + + ui->tableToggleButton->hide(); + ui->cardsToggleButton->show(); + }); + connect(ui->refreshButton, &QPushButton::clicked, [this]() { + currentTab().refresh(); + + if (ui->tabWidget->currentIndex() == PollTab::TabActive) { + ui->pollReceivedLabel->hide(); + } + }); + connect(ui->createPollButton, &QPushButton::clicked, [this]() { + (new PollWizard(*m_voting_model, this))->show(); + }); + connect(ui->tabWidget, &QTabWidget::currentChanged, [this](int tab_id) { + currentTab().filter(m_current_filter); + }); +} + +VotingPage::~VotingPage() +{ + delete ui; +} + +void VotingPage::setVotingModel(VotingModel* model) +{ + m_voting_model = model; + + for (auto tab : m_tabs) { + tab->setVotingModel(model); + } + + if (!model) { + return; + } + + connect(model, &VotingModel::newPollReceived, [this]() { + ui->pollReceivedLabel->show(); + }); +} + +void VotingPage::setOptionsModel(OptionsModel* model) +{ + if (!model) { + return; + } + + connect(model, &OptionsModel::walletStylesheetChanged, this, &VotingPage::updateIcons); + updateIcons(model->getCurrentStyle()); +} + +PollTab& VotingPage::currentTab() +{ + return *qobject_cast(ui->tabWidget->currentWidget()); +} + +void VotingPage::updateIcons(const QString& theme) +{ + m_filter_action->setIcon(QIcon(":/icons/" + theme + "_search")); + ui->cardsToggleButton->setIcon(QIcon(":/icons/" + theme + "_list_view")); + ui->tableToggleButton->setIcon(QIcon(":/icons/" + theme + "_table_view")); + ui->sortButton->setIcon(QIcon(":/icons/" + theme + "_sort_asc")); + ui->refreshButton->setIcon(QIcon(":/icons/" + theme + "_refresh")); + ui->createPollButton->setIcon(QIcon(":/icons/" + theme + "_create")); + + for (auto* tab : m_tabs) { + tab->updateIcons(theme); + } +} + +#include "qt/voting/votingpage.moc" diff --git a/src/qt/voting/votingpage.h b/src/qt/voting/votingpage.h new file mode 100644 index 0000000000..93ee245495 --- /dev/null +++ b/src/qt/voting/votingpage.h @@ -0,0 +1,51 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_VOTINGPAGE_H +#define VOTING_VOTINGPAGE_H + +#include +#include +#include + +namespace Ui { + class VotingPage; +} + +class OptionsModel; +class PollTab; +class VotingModel; + +QT_BEGIN_NAMESPACE +class QAction; +class QResizeEvent; +class QString; +QT_END_NAMESPACE + +class VotingPage : public QWidget +{ + Q_OBJECT + +public: + explicit VotingPage(QWidget* parent = nullptr); + ~VotingPage(); + + void setVotingModel(VotingModel* model); + void setOptionsModel(OptionsModel* model); + + PollTab& currentTab(); + +private: + Ui::VotingPage* ui; + VotingModel* m_voting_model; + std::array m_tabs; + std::unique_ptr m_filter_action; + QString m_current_filter; + + +private slots: + void updateIcons(const QString& theme); +}; + +#endif // VOTING_VOTINGPAGE_H diff --git a/src/qt/votingdialog.cpp b/src/qt/votingdialog.cpp deleted file mode 100644 index 483d47cd4a..0000000000 --- a/src/qt/votingdialog.cpp +++ /dev/null @@ -1,1228 +0,0 @@ -// Copyright (c) 2014-2021 The Gridcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include - -#include -#include -#include - -#ifdef QT_CHARTS_LIB - #include - #include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "util.h" -#include "gridcoin/voting/builders.h" -#include "gridcoin/voting/poll.h" -#include "gridcoin/voting/registry.h" -#include "gridcoin/voting/result.h" -#include "qt/walletmodel.h" -#include "votingdialog.h" -#include "rpc/protocol.h" -#include "sync.h" - -using namespace GRC; - -extern CCriticalSection cs_main; - -static int column_alignments[] = { - Qt::AlignRight|Qt::AlignVCenter, // RowNumber - Qt::AlignLeft|Qt::AlignVCenter, // Expiration - Qt::AlignLeft|Qt::AlignVCenter, // Title - Qt::AlignLeft|Qt::AlignVCenter, // TopAnswer - Qt::AlignRight|Qt::AlignVCenter, // TotalParticipants - Qt::AlignRight|Qt::AlignVCenter, // TotalShares - Qt::AlignLeft|Qt::AlignVCenter, // ShareType - }; - -// VotingTableModel -// -VotingTableModel::VotingTableModel(void) -{ - columns_ - << tr("#") - << tr("Expiration") - << tr("Title") - << tr("Top Answer") - << tr("# Voters") // Total Participants - << tr("Total Shares") - << tr("Share Type") - ; -} - -VotingTableModel::~VotingTableModel(void) -{ - for(ssize_t i=0; i < data_.size(); i++) - if (data_.at(i)) - delete data_.at(i); -} - -int VotingTableModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return data_.size(); -} - -int VotingTableModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return columns_.length(); -} - -QVariant VotingTableModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - const VotingItem *item = (const VotingItem *) index.internalPointer(); - - switch (role) - { - case Qt::DisplayRole: - switch (index.column()) - { - case RowNumber: - return QVariant(item->rowNumber_); - case Title: - return item->title_; - case Expiration: - return item->expiration_.toString(); - case ShareType: - return item->shareType_; - case TotalParticipants: - return item->totalParticipants_; - case TotalShares: - return item->totalShares_; - case TopAnswer: - return item->topAnswer_; - default: - ; - } - break; - - case SortRole: - switch (index.column()) - { - case RowNumber: - return QVariant(item->rowNumber_); - case Title: - return item->title_; - case Expiration: - return item->expiration_; - case ShareType: - return item->shareType_; - case TotalParticipants: - return item->totalParticipants_; - case TotalShares: - return item->totalShares_; - case TopAnswer: - return item->topAnswer_; - default: - ; - } - break; - - case RowNumberRole: - return QVariant(item->rowNumber_); - - case TitleRole: - return item->title_; - - case ExpirationRole: - return item->expiration_; - - case ShareTypeRole: - return item->shareType_; - - case TotalParticipantsRole: - return item->totalParticipants_; - - case TotalSharesRole: - return item->totalShares_; - - case TopAnswerRole: - return item->topAnswer_; - - case Qt::TextAlignmentRole: - return column_alignments[index.column()]; - - default: - ; - } - - return QVariant(); -} - -// section corresponds to column number for horizontal headers -QVariant VotingTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal && section >= 0) - { - if (role == Qt::DisplayRole) - return columns_[section]; - else - if (role == Qt::TextAlignmentRole) - return column_alignments[section]; - else - if (role == Qt::ToolTipRole) - { - switch (section) - { - case RowNumber: - return tr("Row Number."); - case Title: - return tr("Title."); - case Expiration: - return tr("Expiration."); - case ShareType: - return tr("Share Type."); - case TotalParticipants: - return tr("Total Participants."); - case TotalShares: - return tr("Total Shares."); - case TopAnswer: - return tr("Top Answer."); - } - } - } - return QVariant(); -} - -const VotingItem *VotingTableModel::index(int row) const -{ - if ((row >= 0) && (row < data_.size())) - return data_[row]; - return 0; -} - -QModelIndex VotingTableModel::index(int row, int column, const QModelIndex &parent) const -{ - Q_UNUSED(parent); - const VotingItem *item = index(row); - if (item) - return createIndex(row, column, (void *)item); - return QModelIndex(); -} - -Qt::ItemFlags VotingTableModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::ItemIsEnabled; - - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -namespace { -VotingItem* BuildPollItem(const PollRegistry::Sequence::Iterator& iter) -{ - const PollResultOption result = PollResult::BuildFor(iter->Ref()); - - if (!result) { - return nullptr; - } - - const Poll& poll = result->m_poll; - - VotingItem *item = new VotingItem; - item->pollTxid_ = iter->Ref().Txid(); - item->expiration_ = QDateTime::fromMSecsSinceEpoch(poll.Expiration() * 1000); - item->shareType_ = QString::fromStdString(poll.WeightTypeToString()); - item->responseType_ = QString::fromStdString(poll.ResponseTypeToString()); - item->totalParticipants_ = result->m_votes.size(); - item->totalShares_ = result->m_total_weight / (double)COIN; - - item->title_ = QString::fromStdString(poll.m_title).replace("_"," "); - item->question_ = QString::fromStdString(poll.m_question).replace("_"," "); - item->url_ = QString::fromStdString(poll.m_url).trimmed(); - - if (!item->url_.startsWith("http://") && !item->url_.startsWith("https://")) { - item->url_.prepend("http://"); - } - - for (size_t i = 0; i < result->m_responses.size(); ++i) { - item->vectorOfAnswers_.emplace_back( - poll.Choices().At(i)->m_label, - result->m_responses[i].m_weight / (double)COIN, - result->m_responses[i].m_votes); - } - - if (!result->m_votes.empty()) { - item->topAnswer_ = QString::fromStdString(result->WinnerLabel()).replace("_"," "); - } - - return item; -} -} // Anonymous namespace - -void VotingTableModel::resetData(bool history) -{ - std::string function = __func__; - function += ": "; - - // data: erase - if (data_.size()) { - beginRemoveRows(QModelIndex(), 0, data_.size() - 1); - for(ssize_t i=0; i < data_.size(); i++) - if (data_.at(i)) - delete data_.at(i); - data_.clear(); - endRemoveRows(); - } - - g_timer.GetTimes(function + "erase data", "votedialog"); - - // retrieve data - std::vector items; - - { - LOCK(cs_main); - - for (const auto iter : GetPollRegistry().Polls().OnlyActive(!history)) { - if (VotingItem* item = BuildPollItem(iter)) { - item->rowNumber_ = items.size() + 1; - items.push_back(item); - - } - } - - g_timer.GetTimes(function + "populate poll results (cs_main lock)", "votedialog"); - } - - // data: populate - if (items.size()) { - beginInsertRows(QModelIndex(), 0, items.size() - 1); - for(size_t i=0; i < items.size(); i++) - data_.append(items[i]); - endInsertRows(); - - g_timer.GetTimes(function + "insert data in display table", "votedialog"); - } -} - -// VotingProxyModel -// - -VotingProxyModel::VotingProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - setSortRole(VotingTableModel::SortRole); -} - -void VotingProxyModel::setFilterTQAU(const QString &str) -{ - filterTQAU_ = str; - invalidateFilter(); -} - -bool VotingProxyModel::filterAcceptsRow(int row, const QModelIndex &sourceParent) const -{ - QModelIndex index = sourceModel()->index(row, 0, sourceParent); - - QString title = index.data(VotingTableModel::TitleRole).toString(); - - if (!title.contains(filterTQAU_, Qt::CaseInsensitive)){ - return false; - } - return true; -} - -// VotingDialog -// -VotingDialog::VotingDialog(QWidget *parent) - : QWidget(parent), - tableView_(0), - tableModel_(0), - proxyModel_(0), - chartDialog_(0), - voteDialog_(0) -{ - // data - tableModel_ = new VotingTableModel(); - proxyModel_ = new VotingProxyModel(this); - proxyModel_->setSourceModel(tableModel_); - - QVBoxLayout *vlayout = new QVBoxLayout(this); - - QGroupBox *groupBox = new QGroupBox(tr("Active Polls (Right Click to Vote)")); - vlayout->addWidget(groupBox); - - QVBoxLayout *groupboxvlayout = new QVBoxLayout(); - groupBox->setLayout(groupboxvlayout); - - QHBoxLayout *filterhlayout = new QHBoxLayout(); - groupboxvlayout->addLayout(filterhlayout); - - // Filter by Title with one QLineEdit - QLabel *filterByTQAULabel = new QLabel(tr("Filter: ")); - filterByTQAULabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - filterByTQAULabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - filterByTQAULabel->setMaximumWidth(150); - filterhlayout->addWidget(filterByTQAULabel); - filterTQAU = new QLineEdit(); - filterTQAU->setMaximumWidth(350); - filterhlayout->addWidget(filterTQAU); - connect(filterTQAU, SIGNAL(textChanged(QString)), this, SLOT(filterTQAUChanged(QString))); - filterhlayout->addStretch(); - - // buttons in horizontal layout - QHBoxLayout *groupboxhlayout = new QHBoxLayout(); - groupboxvlayout->addLayout(groupboxhlayout); - - QPushButton *resetButton = new QPushButton(); - resetButton->setText(tr("Reload Polls")); - groupboxhlayout->addWidget(resetButton); - connect(resetButton, SIGNAL(clicked()), this, SLOT(resetData())); - - QPushButton *histButton = new QPushButton(); - histButton->setText(tr("Load History")); - groupboxhlayout->addWidget(histButton); - connect(histButton, SIGNAL(clicked()), this, SLOT(loadHistory())); - - QPushButton *newPollButton = new QPushButton(); - newPollButton->setText(tr("Create Poll")); - groupboxhlayout->addWidget(newPollButton); - connect(newPollButton, SIGNAL(clicked()), this, SLOT(showNewPollDialog())); - - groupboxhlayout->addStretch(); - - tableView_ = new QTableView(); - tableView_->installEventFilter(this); - tableView_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - // tableView_->setTabKeyNavigation(false); - tableView_->setContextMenuPolicy(Qt::CustomContextMenu); - connect(tableView_, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(showContextMenu(const QPoint &))); - tableView_->setAlternatingRowColors(true); - tableView_->setSelectionMode(QAbstractItemView::ExtendedSelection); - tableView_->setSortingEnabled(true); - tableView_->sortByColumn(VotingTableModel::RowNumber, Qt::DescendingOrder); - tableView_->verticalHeader()->hide(); - tableView_->setShowGrid(false); - - tableView_->setModel(proxyModel_); - tableView_->setFont(QFont("Arial", 10)); - tableView_->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - tableView_->horizontalHeader()->setMinimumWidth(VOTINGDIALOG_WIDTH_RowNumber - + VOTINGDIALOG_WIDTH_Title - + VOTINGDIALOG_WIDTH_Expiration - + VOTINGDIALOG_WIDTH_ShareType - + VOTINGDIALOG_WIDTH_TotalParticipants - + VOTINGDIALOG_WIDTH_TotalShares - + VOTINGDIALOG_WIDTH_TopAnswer); - tableView_->verticalHeader()->setDefaultSectionSize(40); - - groupboxvlayout->addWidget(tableView_); - - // loading overlay. Due to a bug in QFutureWatcher for Qt <5.6.0 we - // have to track the running state ourselves. See - // https://bugreports.qt.io/browse/QTBUG-12358 - watcher.setProperty("running", false); - connect(&watcher, SIGNAL(finished()), this, SLOT(onLoadingFinished())); - loadingIndicator = new QLabel(this); - loadingIndicator->setWordWrap(true); - - groupboxvlayout->addWidget(loadingIndicator); - - chartDialog_ = new VotingChartDialog(this); - voteDialog_ = new VotingVoteDialog(this); - pollDialog_ = new NewPollDialog(this); - - loadingIndicator->setText(tr("Press reload to load polls... This can take several minutes, and the wallet may not " - "respond until finished.")); - tableView_->hide(); - loadingIndicator->show(); - - QObject::connect(vote_update_age_timer, SIGNAL(timeout()), this, SLOT(setStale())); -} - -void VotingDialog::setModel(WalletModel *wallet_model) -{ - if (!wallet_model) { - return; - } - - voteDialog_->setModel(wallet_model); - pollDialog_->setModel(wallet_model); -} - -void VotingDialog::loadPolls(bool history) -{ - std::string function = __func__; - function += ": "; - - bool isRunning = watcher.property("running").toBool(); - if (tableModel_&& !isRunning) - { - loadingIndicator->setText(tr("Recalculating voting weights... This can take several minutes, and the wallet may not " - "respond until finished.")); - tableView_->hide(); - loadingIndicator->show(); - - g_timer.InitTimer("votedialog", LogInstance().WillLogCategory(BCLog::LogFlags::MISC)); - vote_update_age_timer->start(STALE); - - QFuture future = QtConcurrent::run(tableModel_, &VotingTableModel::resetData, history); - - g_timer.GetTimes(function + "Post future assignment", "votedialog"); - - watcher.setProperty("running", true); - watcher.setFuture(future); - } -} - -void VotingDialog::resetData(void) -{ - loadPolls(false); -} - -void VotingDialog::loadHistory(void) -{ - loadPolls(true); -} - -void VotingDialog::setStale(void) -{ - LogPrint(BCLog::LogFlags::MISC, "INFO: %s called.", __func__); - - // If the stale flag is not set, but this function is called, it is from the timeout - // trigger of the vote_update_age_timer. Therefore set the loading indicator to stale - // and set the stale flag to true. The stale flag will be reset and the timer restarted - // when the loadPolls is called to refresh. - if (!stale) - { - loadingIndicator->setText(tr("Poll data is more than one hour old. Press reload to update... " - "This can take several minutes, and the wallet may not respond " - "until finished.")); - tableView_->hide(); - loadingIndicator->show(); - - stale = true; - } -} - -void VotingDialog::onLoadingFinished(void) -{ - watcher.setProperty("running", false); - - int rowsCount = tableView_->verticalHeader()->count(); - if (rowsCount > 0) { - loadingIndicator->hide(); - tableView_->show(); - } else { - loadingIndicator->setText(tr("No polls !")); - } - - stale = false; -} - -void VotingDialog::tableColResize(void) -{ - tableView_->setColumnWidth(VotingTableModel::RowNumber, VOTINGDIALOG_WIDTH_RowNumber); - tableView_->setColumnWidth(VotingTableModel::Expiration, VOTINGDIALOG_WIDTH_Expiration); - tableView_->setColumnWidth(VotingTableModel::ShareType, VOTINGDIALOG_WIDTH_ShareType); - tableView_->setColumnWidth(VotingTableModel::TotalParticipants, VOTINGDIALOG_WIDTH_TotalParticipants); - tableView_->setColumnWidth(VotingTableModel::TotalShares, VOTINGDIALOG_WIDTH_TotalShares); - - int fixedColWidth = VOTINGDIALOG_WIDTH_RowNumber - + VOTINGDIALOG_WIDTH_Expiration - + VOTINGDIALOG_WIDTH_ShareType - + VOTINGDIALOG_WIDTH_TotalParticipants - + VOTINGDIALOG_WIDTH_TotalShares; - - int dynamicWidth = tableView_->horizontalHeader()->width() - fixedColWidth; - int nColumns = 2; // 2 dynamic columns - int columns[] = {VotingTableModel::Title,VotingTableModel::TopAnswer}; - int remainingWidth = dynamicWidth % nColumns; - for(int cNum = 0; cNum < nColumns; cNum++) { - if(remainingWidth > 0) - { - tableView_->setColumnWidth(columns[cNum], (dynamicWidth/nColumns) + 1); - remainingWidth -= 1; - } - else - { - tableView_->setColumnWidth(columns[cNum], dynamicWidth/nColumns); - } - } -} - -//customize resize event to allow automatic as well as interactive resizing -void VotingDialog::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - - tableColResize(); -} - -//customize show event for instant table resize -void VotingDialog::showEvent(QShowEvent *event) -{ - QWidget::showEvent(event); - - if (! event->spontaneous()) - tableColResize(); -} - -bool VotingDialog::eventFilter(QObject *obj, QEvent *event) -{ - if (obj == tableView_) - { - if (event->type() == QEvent::KeyPress) - { - QKeyEvent *ke = static_cast(event); - if ((ke->key() == Qt::Key_C) - && (ke->modifiers().testFlag(Qt::ControlModifier))) - { - /* Ctrl-C: copy the selected cells in TableModel */ - QString selected_text; - QItemSelectionModel *selection = tableView_->selectionModel(); - QModelIndexList indexes = selection->selectedIndexes(); - std::sort(indexes.begin(), indexes.end()); - int prev_row = -1; - for(int i=0; i < indexes.size(); i++) { - QModelIndex index = indexes.at(i); - if (i) { - char c = (index.row() != prev_row)? '\n': '\t'; - selected_text.append(c); - } - QVariant data = tableView_->model()->data(index); - selected_text.append( data.toString() ); - prev_row = index.row(); - } - QApplication::clipboard()->setText(selected_text); - return true; - } - } - } - - return QWidget::eventFilter(obj, event); -} - -void VotingDialog::filterTQAUChanged(const QString &str) -{ - if (proxyModel_) - proxyModel_->setFilterTQAU(str); -} - -void VotingDialog::showChartDialog(void) -{ - if (!proxyModel_ || !tableModel_ || !tableView_ || !chartDialog_) - return; - - QItemSelectionModel *selection = tableView_->selectionModel(); - QModelIndexList indexes = selection->selectedIndexes(); - if (!indexes.size()) - return; - - // take the row of the top selected cell - std::sort(indexes.begin(), indexes.end()); - int row = proxyModel_->mapToSource(indexes.at(0)).row(); - - // reset the dialog's data - const VotingItem *item = tableModel_->index(row); - chartDialog_->resetData(item); - - chartDialog_->show(); - chartDialog_->raise(); - chartDialog_->setFocus(); -} - -void VotingDialog::showContextMenu(const QPoint &pos) -{ - QPoint globalPos = tableView_->viewport()->mapToGlobal(pos); - - QMenu menu; - menu.addAction("Show Results", this, SLOT(showChartDialog())); - menu.addAction("Vote", this, SLOT(showVoteDialog())); - menu.exec(globalPos); -} - -void VotingDialog::showVoteDialog(void) -{ - if (!proxyModel_ || !tableModel_ || !tableView_ || !voteDialog_) - return; - - QItemSelectionModel *selection = tableView_->selectionModel(); - QModelIndexList indexes = selection->selectedIndexes(); - if (!indexes.size()) - return; - - // take the row of the top selected cell - std::sort(indexes.begin(), indexes.end()); - int row = proxyModel_->mapToSource(indexes.at(0)).row(); - - // reset the dialog's data - const VotingItem *item = tableModel_->index(row); - voteDialog_->resetData(item); - - voteDialog_->show(); - voteDialog_->raise(); - voteDialog_->setFocus(); -} - -void VotingDialog::showNewPollDialog(void) -{ - if (!proxyModel_ || !tableModel_ || !tableView_ || !voteDialog_) - return; - - // reset the dialog's data - pollDialog_->resetData(); - - pollDialog_->show(); - pollDialog_->raise(); - pollDialog_->setFocus(); -} - -// VotingChartDialog -// -VotingChartDialog::VotingChartDialog(QWidget *parent) - : QDialog(parent) -#ifdef QT_CHARTS_LIB - ,chart_(0) -#endif - ,answerTable_(NULL) -{ - setWindowTitle(tr("Poll Results")); - resize(QDesktopWidget().availableGeometry(this).size() * 0.4); - - QVBoxLayout *vlayout = new QVBoxLayout(this); - - QGridLayout *glayout = new QGridLayout(); - glayout->setHorizontalSpacing(0); - glayout->setVerticalSpacing(0); - glayout->setColumnStretch(0, 1); - glayout->setColumnStretch(1, 3); - glayout->setColumnStretch(2, 5); - - vlayout->addLayout(glayout); - - QLabel *question = new QLabel(tr("Q: ")); - question->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - question->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(question, 0, 0); - - question_ = new QLabel(); - question_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - question_->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(question_, 0, 1); - - QLabel *discussionLabel = new QLabel(tr("Discussion URL: ")); - discussionLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - discussionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(discussionLabel, 1, 0); - - url_ = new QLabel(); - url_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - url_->setTextFormat(Qt::RichText); - url_->setTextInteractionFlags(Qt::TextBrowserInteraction); - url_->setOpenExternalLinks(true); - glayout->addWidget(url_, 1, 1); - - QLabel *topAnswer = new QLabel(tr("Top Answer: ")); - topAnswer->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - topAnswer->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(topAnswer, 3, 0); - - answer_ = new QLabel(); - answer_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - answer_->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(answer_, 3, 1); - - QTabWidget *resTabWidget = new QTabWidget; - -#ifdef QT_CHARTS_LIB - chart_ = new QtCharts::QChart; - chart_->legend()->setVisible(true); - chart_->legend()->setAlignment(Qt::AlignRight); - QtCharts::QChartView *m_chartView = new QtCharts::QChartView(chart_); - m_chartView->setRenderHint(QPainter::Antialiasing); - resTabWidget->addTab(m_chartView, tr("Chart")); -#endif - - answerModel_ = new QStandardItemModel(); - answerModel_->setColumnCount(3); - answerModel_->setRowCount(0); - answerModel_->setHeaderData(0, Qt::Horizontal, tr("Answer")); - answerModel_->setHeaderData(1, Qt::Horizontal, tr("Shares")); - answerModel_->setHeaderData(2, Qt::Horizontal, "%"); - - answerTable_ = new QTableView(this); - answerTable_->setModel(answerModel_); - answerTable_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - answerTable_->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::ResizeToContents); - answerTable_->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents); - answerTable_->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); - answerTable_->setEditTriggers( QAbstractItemView::NoEditTriggers ); - resTabWidget->addTab(answerTable_, tr("List")); - vlayout->addWidget(resTabWidget); -} - -void VotingChartDialog::resetData(const VotingItem *item) -{ - if (!item) - return; - - answerModel_->setRowCount(0); - answerTable_->sortByColumn(-1, Qt::AscendingOrder); - answerTable_->setSortingEnabled(false); - -#ifdef QT_CHARTS_LIB - QList oldSeriesList = chart_->series(); - foreach (QtCharts::QAbstractSeries *oldSeries, oldSeriesList) - chart_->removeSeries(oldSeries); - - QtCharts::QPieSeries *series = new QtCharts::QPieSeries(); -#endif - - question_->setText(item->question_); - url_->setText("url_+"\">"+item->url_+""); - answer_->setText(item->topAnswer_); - answer_->setVisible(!item->topAnswer_.isEmpty()); - answerModel_->setRowCount(item->vectorOfAnswers_.size()); - - for (size_t y = 0; y < item->vectorOfAnswers_.size(); y++) - { - const auto& responses = item->vectorOfAnswers_; - const QString answer = QString::fromStdString(responses[y].answer); - - QStandardItem *answerItem = new QStandardItem(answer); - answerItem->setData(answer); - answerModel_->setItem(y, 0, answerItem); - QStandardItem *iSharesItem = new QStandardItem(QString::number(responses[y].shares, 'f', 0)); - iSharesItem->setData(responses[y].shares); - iSharesItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - answerModel_->setItem(y, 1, iSharesItem); - QStandardItem *percentItem = new QStandardItem(); - - if (item->totalShares_ > 0) { - const double ratio = responses[y].shares / (double)item->totalShares_; - percentItem->setText(QString::number(ratio * 100, 'f', 2)); - percentItem->setData(ratio * 100); - percentItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); - } - - answerModel_->setItem(y, 2, percentItem); - -#ifdef QT_CHARTS_LIB - QtCharts::QPieSlice *slice = new QtCharts::QPieSlice(answer, responses[y].shares); - series->append(slice); - chart_->addSeries(series); -#endif - } - - answerModel_->setSortRole(Qt::UserRole+1); - answerTable_->setSortingEnabled(true); -} - -// VotingVoteDialog -// -VotingVoteDialog::VotingVoteDialog(QWidget *parent) - : QDialog(parent) - , m_wallet_model(nullptr) -{ - setWindowTitle(tr("PlaceVote")); - resize(QDesktopWidget().availableGeometry(this).size() * 0.4); - - QVBoxLayout *vlayout = new QVBoxLayout(this); - - QGridLayout *glayout = new QGridLayout(); - glayout->setHorizontalSpacing(0); - glayout->setVerticalSpacing(0); - glayout->setColumnStretch(0, 1); - glayout->setColumnStretch(1, 3); - glayout->setColumnStretch(2, 5); - - vlayout->addLayout(glayout); - - QLabel *question = new QLabel(tr("Q: ")); - question->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - question->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(question, 0, 0); - - question_ = new QLabel(); - question_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - question_->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(question_, 0, 1); - - QLabel *discussionLabel = new QLabel(tr("Discussion URL: ")); - discussionLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - discussionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(discussionLabel, 1, 0); - - url_ = new QLabel(); - url_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - url_->setTextFormat(Qt::RichText); - url_->setTextInteractionFlags(Qt::TextBrowserInteraction); - url_->setOpenExternalLinks(true); - glayout->addWidget(url_, 1, 1); - - QLabel *responseTypeLabel = new QLabel(tr("Response Type: ")); - responseTypeLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - responseTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(responseTypeLabel, 3, 0); - - responseType_ = new QLabel(); - responseType_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - responseType_->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(responseType_, 3, 1); - - QLabel *topAnswer = new QLabel(tr("Top Answer: ")); - topAnswer->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - topAnswer->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(topAnswer, 4, 0); - - answer_ = new QLabel(); - answer_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - answer_->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(answer_, 4, 1); - - answerList_ = new QListWidget(this); - vlayout->addWidget(answerList_); - - QHBoxLayout *hlayout = new QHBoxLayout(); - vlayout->addLayout(hlayout); - - QPushButton *voteButton = new QPushButton(); - voteButton->setText(tr("Vote")); - hlayout->addWidget(voteButton); - connect(voteButton, SIGNAL(clicked()), this, SLOT(vote())); - - voteNote_ = new QLabel(); - voteNote_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - voteNote_->setTextInteractionFlags(Qt::TextSelectableByMouse); - voteNote_->setWordWrap(true); - hlayout->addWidget(voteNote_); - -} - -void VotingVoteDialog::setModel(WalletModel *wallet_model) -{ - if (!wallet_model) { - return; - } - - m_wallet_model = wallet_model; -} - -void VotingVoteDialog::resetData(const VotingItem *item) -{ - if (!item) - return; - - answerList_->clear(); - voteNote_->clear(); - question_->setText(item->question_); - url_->setText("url_+"\">"+item->url_+""); - responseType_->setText(item->responseType_); - answer_->setText(item->topAnswer_); - pollTxid_ = item->pollTxid_; - - for (const auto& choice : item->vectorOfAnswers_) { - QListWidgetItem *answerItem = new QListWidgetItem(QString::fromStdString(choice.answer).replace("_", " "), answerList_); - answerItem->setCheckState(Qt::Unchecked); - } -} - -void VotingVoteDialog::vote(void) -{ - // This overall try-catch is needed to properly catch the VoteBuilder builder move constructor and assignment, - // otherwise an expired poll bubbles up all the way to the app level and ends execution with the exception handler - // in bitcoin.cpp, which is not what is intended here. It also catches any thrown VotingError exceptions in - // builder.AddResponse() and SendVoteContract(). - try { - voteNote_->setStyleSheet("QLabel { color : red; }"); - - LOCK(cs_main); - - const PollReference* ref = GetPollRegistry().TryByTxid(pollTxid_); - - if (!ref) { - voteNote_->setText(tr("Poll not found.")); - return; - } - - const PollOption poll = ref->TryReadFromDisk(); - - if (!poll) { - voteNote_->setText(tr("Failed to load poll from disk")); - return; - } - - VoteBuilder builder = VoteBuilder::ForPoll(*poll, ref->Txid()); - - for (int row = 0; row < answerList_->count(); ++row) { - if (answerList_->item(row)->checkState() == Qt::Checked) { - builder = builder.AddResponse(row); - } - } - - const WalletModel::UnlockContext unlock_context(m_wallet_model->requestUnlock()); - - if (!unlock_context.isValid()) { - voteNote_->setText(tr("Please unlock the wallet.")); - return; - } - - SendVoteContract(std::move(builder)); - - voteNote_->setStyleSheet("QLabel { color : green; }"); - voteNote_->setText(tr("Success. Vote will activate with the next block.")); - } catch (const VotingError& e){ - voteNote_->setText(e.what()); - return; - } -} - -NewPollDialog::NewPollDialog(QWidget *parent) - : QDialog(parent) - , m_wallet_model(nullptr) -{ - setWindowTitle(tr("Create Poll")); - resize(QDesktopWidget().availableGeometry(this).size() * 0.4); - - QVBoxLayout *vlayout = new QVBoxLayout(this); - - QGridLayout *glayout = new QGridLayout(); - glayout->setHorizontalSpacing(0); - glayout->setVerticalSpacing(5); - glayout->setColumnStretch(0, 1); - glayout->setColumnStretch(1, 3); - glayout->setColumnStretch(2, 5); - - vlayout->addLayout(glayout); - - //title - QLabel *title = new QLabel(tr("Title: ")); - title->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - title->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(title, 0, 0); - - title_ = new QLineEdit(); - title_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - glayout->addWidget(title_, 0, 1); - - //days - QLabel *days = new QLabel(tr("Days: ")); - days->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - days->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(days, 1, 0); - - days_ = new QLineEdit(); - days_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - glayout->addWidget(days_, 1, 1); - - //question - QLabel *question = new QLabel(tr("Question: ")); - question->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - question->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(question, 2, 0); - - question_ = new QLineEdit(); - question_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - glayout->addWidget(question_, 2, 1); - - //url - QLabel *discussionLabel = new QLabel(tr("Discussion URL: ")); - discussionLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - discussionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(discussionLabel, 3, 0); - - url_ = new QLineEdit(); - url_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - glayout->addWidget(url_, 3, 1); - - //share type - QLabel *shareType = new QLabel(tr("Share Type: ")); - shareType->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - shareType->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(shareType, 4, 0); - - shareTypeBox_ = new QComboBox(this); - QStringList shareTypeBoxItems; - shareTypeBoxItems << tr("Balance") << tr("Magnitude+Balance"); - shareTypeBox_->addItems(shareTypeBoxItems); - glayout->addWidget(shareTypeBox_, 4, 1); - - // response type - QLabel *responseTypeLabel = new QLabel(tr("Response Type: ")); - responseTypeLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - responseTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(responseTypeLabel, 5, 0); - - responseTypeBox_ = new QComboBox(this); - QStringList responseTypeBoxItems; - responseTypeBoxItems - << tr("Yes/No/Abstain") - << tr("Single Choice") - << tr("Multiple Choice"); - responseTypeBox_->addItems(responseTypeBoxItems); - glayout->addWidget(responseTypeBox_, 5, 1); - - // cost - QLabel *costLabelLabel = new QLabel(tr("Cost:")); - costLabelLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - costLabelLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(costLabelLabel, 6, 0); - - // TODO: make this dynamic when rewriting the voting GUI: - QLabel *costLabel = new QLabel(tr("50 GRC + transaction fee")); - costLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - costLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - glayout->addWidget(costLabel, 6, 1); - - //answers - answerList_ = new QListWidget(this); - answerList_->setContextMenuPolicy(Qt::CustomContextMenu); - connect(answerList_, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showContextMenu(const QPoint &))); - vlayout->addWidget(answerList_); - connect (answerList_, SIGNAL (itemDoubleClicked (QListWidgetItem *)), this, SLOT (editItem (QListWidgetItem *))); - - QHBoxLayout *hlayoutTools = new QHBoxLayout(); - vlayout->addLayout(hlayoutTools); - - QPushButton *addItemButton = new QPushButton(); - addItemButton->setText(tr("Add Item")); - hlayoutTools->addWidget(addItemButton); - connect(addItemButton, SIGNAL(clicked()), this, SLOT(addItem())); - - QPushButton *removeItemButton = new QPushButton(); - removeItemButton->setText(tr("Remove Item")); - hlayoutTools->addWidget(removeItemButton); - connect(removeItemButton, SIGNAL(clicked()), this, SLOT(removeItem())); - - QPushButton *clearAllButton = new QPushButton(); - clearAllButton->setText(tr("Clear All")); - hlayoutTools->addWidget(clearAllButton); - connect(clearAllButton, SIGNAL(clicked()), this, SLOT(resetData())); - - QHBoxLayout *hlayoutBottom = new QHBoxLayout(); - vlayout->addLayout(hlayoutBottom); - - QPushButton *pollButton = new QPushButton(); - pollButton->setText(tr("Create Poll")); - hlayoutBottom->addWidget(pollButton); - connect(pollButton, SIGNAL(clicked()), this, SLOT(createPoll())); - - pollNote_ = new QLabel(); - pollNote_->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - pollNote_->setTextInteractionFlags(Qt::TextSelectableByMouse); - pollNote_->setWordWrap(true); - hlayoutBottom->addWidget(pollNote_); - -} - -void NewPollDialog::setModel(WalletModel *wallet_model) -{ - if (!wallet_model) { - return; - } - - m_wallet_model = wallet_model; -} - -void NewPollDialog::resetData() -{ - answerList_->clear(); - pollNote_->clear(); - title_->clear(); - days_->clear(); - question_->clear(); - url_->clear(); -} - -void NewPollDialog::createPoll(void) -{ - pollNote_->setStyleSheet("QLabel { color : red; }"); - PollBuilder builder = PollBuilder(); - - try { - builder = builder - .SetType(PollType::SURVEY) - .SetTitle(title_->text().toStdString()) - .SetDuration(days_->text().toInt()) - .SetQuestion(question_->text().toStdString()) - // The dropdown list only contains non-deprecated weight type - // options which start from offset 2: - .SetWeightType(shareTypeBox_->currentIndex() + 2) - .SetResponseType(responseTypeBox_->currentIndex() + 1) - .SetUrl(url_->text().toStdString()); - - for (int row = 0; row < answerList_->count(); ++row) { - const QListWidgetItem* const item = answerList_->item(row); - builder = builder.AddChoice(item->text().toStdString()); - } - } catch (const VotingError& e) { - pollNote_->setText(e.what()); - return; - } - - const WalletModel::UnlockContext unlock_context(m_wallet_model->requestUnlock()); - - if (!unlock_context.isValid()) { - pollNote_->setText(tr("Please unlock the wallet.")); - return; - } - - try { - SendPollContract(std::move(builder)); - } catch (const VotingError& e) { - pollNote_->setText(e.what()); - return; - } - - pollNote_->setStyleSheet("QLabel { color : green; }"); - pollNote_->setText("Success. The poll will activate with the next block."); -} - -void NewPollDialog::addItem (void) -{ - QListWidgetItem *answerItem = new QListWidgetItem("New Item",answerList_); - answerItem->setFlags (answerItem->flags() | Qt::ItemIsEditable); -} - -void NewPollDialog::editItem (QListWidgetItem *item) -{ - answerList_->editItem(item); -} - -void NewPollDialog::removeItem(void) -{ - QList items = answerList_->selectedItems(); - foreach(QListWidgetItem * item, items) - { - delete answerList_->takeItem(answerList_->row(item)); - } - -} - -void NewPollDialog::showContextMenu(const QPoint &pos) -{ - QPoint globalPos = answerList_->viewport()->mapToGlobal(pos); - - QMenu menu; - menu.addAction("Add Item", this, SLOT(addItem())); - menu.addAction("Remove Item", this, SLOT(removeItem())); - menu.exec(globalPos); -} diff --git a/src/qt/votingdialog.h b/src/qt/votingdialog.h deleted file mode 100644 index 7299e7a1ea..0000000000 --- a/src/qt/votingdialog.h +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (c) 2014-2021 The Gridcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef VOTINGDIALOG_H -#define VOTINGDIALOG_H - -#include "uint256.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef QT_CHARTS_LIB -#include -#include -QT_CHARTS_BEGIN_NAMESPACE -class QChart; -QT_CHARTS_END_NAMESPACE -#endif - -QT_BEGIN_NAMESPACE -class QEvent; -class QObject; -class QResizeEvent; -QT_END_NAMESPACE - -class WalletModel; - -#define VOTINGDIALOG_WIDTH_RowNumber 40 -#define VOTINGDIALOG_WIDTH_Title 225 -#define VOTINGDIALOG_WIDTH_Expiration 175 -#define VOTINGDIALOG_WIDTH_ShareType 80 -#define VOTINGDIALOG_WIDTH_TotalParticipants 80 -#define VOTINGDIALOG_WIDTH_TotalShares 100 -#define VOTINGDIALOG_WIDTH_TopAnswer 80 - -namespace polling { -// TODO: Legacy struct moved here until we redesign the voting GUI. -struct Vote { - std::string answer; - double shares; - double participants; - - Vote(std::string answer, double shares, double participants) - : answer(std::move(answer)) - , shares(shares) - , participants(participants) - { - } -}; -} - -class VotingItem { -public: - unsigned int rowNumber_; - uint256 pollTxid_; - QString title_; - QDateTime expiration_; - QString shareType_; - QString responseType_; - QString question_; - std::vector vectorOfAnswers_; - unsigned int totalParticipants_; - unsigned int totalShares_; - QString url_; - QString topAnswer_; -}; - -// VotingTableModel -// -class VotingTableModel - : public QAbstractTableModel -{ - Q_OBJECT - -public: - explicit VotingTableModel(); - ~VotingTableModel(); - - enum ColumnIndex { - RowNumber = 0, - Expiration = 1, - Title = 2, - TopAnswer = 3, - TotalParticipants = 4, - TotalShares = 5, - ShareType = 6, - }; - - enum Roles { - RowNumberRole = Qt::UserRole, - ExpirationRole, - TitleRole, - TopAnswerRole, - TotalParticipantsRole, - TotalSharesRole, - ShareTypeRole, - - SortRole, - }; - - int rowCount(const QModelIndex &) const; - int columnCount(const QModelIndex &) const; - QVariant data(const QModelIndex &, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - const VotingItem *index(int row) const; - QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &) const; - void resetData(bool history); - -private: - QStringList columns_; - QList data_; -}; - - -// VotingProxyModel -// -class VotingProxyModel - : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - explicit VotingProxyModel(QObject *parent=0); - void setFilterTQAU(const QString &); // filter Title - -protected: - bool filterAcceptsRow(int, const QModelIndex &) const; - -private: - QString filterTQAU_; -}; - -class VotingChartDialog; -class VotingVoteDialog; -class NewPollDialog; - -// VotingDialog -// -class VotingDialog - : public QWidget -{ - Q_OBJECT - -public: - explicit VotingDialog(QWidget *parent=0); - void setModel(WalletModel *wallet_model); - -private: - // The number of milliseconds of age at which the poll data is stale. Currently one hour equivalent. - static constexpr int64_t STALE = 60 * 60 * 1000; - - QLineEdit *filterTQAU; - QPushButton *resetButton; - QPushButton *histButton; - QPushButton *newPollButton; - QTableView *tableView_; - VotingTableModel *tableModel_; - VotingProxyModel *proxyModel_; - VotingChartDialog *chartDialog_; - VotingVoteDialog *voteDialog_; - NewPollDialog *pollDialog_; - QLabel *loadingIndicator; - QFutureWatcher watcher; - QTimer* vote_update_age_timer = new QTimer(this); - bool stale = false; - -private: - virtual void showEvent(QShowEvent *); - virtual void resizeEvent(QResizeEvent *); - void tableColResize(void); - bool eventFilter(QObject *, QEvent *); - -private slots: - void onLoadingFinished(void); - void setStale(void); - -public slots: - void filterTQAUChanged(const QString &); - void loadPolls(bool history); - void resetData(void); - void loadHistory(void); - void showChartDialog(void); - void showContextMenu(const QPoint &); - void showVoteDialog(void); - void showNewPollDialog(void); -}; - -// VotingChartDialog -// -class VotingChartDialog - : public QDialog -{ - Q_OBJECT - -public: - explicit VotingChartDialog(QWidget *parent=0); - void resetData(const VotingItem *); - -private: - QLabel *question_; - QLabel *url_; -#ifdef QT_CHARTS_LIB - QtCharts::QChart *chart_; -#endif - QTableView *answerTable_; - QStandardItemModel *answerModel_; - QStringList answerTableHeader; - QLabel *answer_; -}; - -// VotingVoteDialog -// -class VotingVoteDialog - : public QDialog -{ - Q_OBJECT - -public: - explicit VotingVoteDialog(QWidget *parent=0); - void setModel(WalletModel *wallet_model); - void resetData(const VotingItem *); - -private: - WalletModel *m_wallet_model; - QLabel *question_; - QLabel *url_; - QLabel *responseType_; - QLabel *answer_; - QLabel *voteNote_; - QListWidget *answerList_; - QListWidgetItem *answerItem; - QPushButton *voteButton; - uint256 pollTxid_; - -private slots: - void vote(void); -}; - -// NewPollDialog -// -class NewPollDialog - : public QDialog -{ - Q_OBJECT - -public: - explicit NewPollDialog(QWidget *parent=0); - void setModel(WalletModel *wallet_model); - -public slots: - void resetData(void); - -private: - WalletModel *m_wallet_model; - QLineEdit *title_; - QLineEdit *days_; - QLineEdit *question_; - QLineEdit *url_; - QComboBox *shareTypeBox_; - QComboBox *responseTypeBox_; - QLabel *pollNote_; - QListWidget *answerList_; - QListWidgetItem *answerItem; - QPushButton *addItemButton; - QPushButton *removeItemButton; - QPushButton *clearAllButton; - QPushButton *pollButton; - -private slots: - void createPoll(void); - void editItem (QListWidgetItem *item); - void addItem (void); - void removeItem(void); - void showContextMenu(const QPoint &); -}; - -#endif // VOTINGDIALOG_H From 9588b8c1db6968d94b579438f5755e24798748d4 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:49 -0500 Subject: [PATCH 170/280] Create a vote submission wizard This adds a wizard that replaces the old "vote" dialog and displays a more pleasant voting form. It does not add new features yet. However, the wizard format provides a structure which can enable more advanced voting features in the future. --- gridcoinresearch.pro | 9 + src/Makefile.qt.include | 12 ++ src/qt/forms/voting/votewizard.ui | 53 ++++++ src/qt/forms/voting/votewizardballotpage.ui | 118 +++++++++++++ src/qt/forms/voting/votewizardsummarypage.ui | 175 +++++++++++++++++++ src/qt/voting/votewizard.cpp | 31 ++++ src/qt/voting/votewizard.h | 38 ++++ src/qt/voting/votewizardballotpage.cpp | 113 ++++++++++++ src/qt/voting/votewizardballotpage.h | 44 +++++ src/qt/voting/votewizardsummarypage.cpp | 46 +++++ src/qt/voting/votewizardsummarypage.h | 35 ++++ 11 files changed, 674 insertions(+) create mode 100644 src/qt/forms/voting/votewizard.ui create mode 100644 src/qt/forms/voting/votewizardballotpage.ui create mode 100644 src/qt/forms/voting/votewizardsummarypage.ui create mode 100644 src/qt/voting/votewizard.cpp create mode 100644 src/qt/voting/votewizard.h create mode 100644 src/qt/voting/votewizardballotpage.cpp create mode 100644 src/qt/voting/votewizardballotpage.h create mode 100644 src/qt/voting/votewizardsummarypage.cpp create mode 100644 src/qt/voting/votewizardsummarypage.h diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 4e94242a82..5158676698 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -197,6 +197,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/voting/pollwizardprojectpage.h \ src/qt/voting/pollwizardsummarypage.h \ src/qt/voting/pollwizardtypepage.h \ + src/qt/voting/votewizard.h \ + src/qt/voting/votewizardballotpage.h \ + src/qt/voting/votewizardsummarypage.h \ src/qt/voting/votingmodel.h \ src/qt/voting/votingpage.h \ src/qt/transactiontablemodel.h \ @@ -317,6 +320,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/voting/pollwizardprojectpage.cpp \ src/qt/voting/pollwizardsummarypage.cpp \ src/qt/voting/pollwizardtypepage.cpp \ + src/qt/voting/votewizard.cpp \ + src/qt/voting/votewizardballotpage.cpp \ + src/qt/voting/votewizardsummarypage.cpp \ src/qt/voting/votingmodel.cpp \ src/qt/voting/votingpage.cpp \ src/qt/transactiontablemodel.cpp \ @@ -432,6 +438,9 @@ FORMS += \ src/qt/forms/voting/pollwizardprojectpage.ui \ src/qt/forms/voting/pollwizardsummarypage.ui \ src/qt/forms/voting/pollwizardtypepage.ui \ + src/qt/forms/voting/votewizard.ui \ + src/qt/forms/voting/votewizardballotpage.ui \ + src/qt/forms/voting/votewizardsummarypage.ui \ src/qt/forms/voting/votingpage.ui \ src/qt/forms/receivecoinspage.ui \ src/qt/forms/sendcoinsdialog.ui \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 8a1645f2e6..5da3c14f58 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -116,6 +116,9 @@ QT_FORMS_UI = \ qt/forms/voting/pollwizardprojectpage.ui \ qt/forms/voting/pollwizardsummarypage.ui \ qt/forms/voting/pollwizardtypepage.ui \ + qt/forms/voting/votewizard.ui \ + qt/forms/voting/votewizardballotpage.ui \ + qt/forms/voting/votewizardsummarypage.ui \ qt/forms/voting/votingpage.ui QT_MOC_CPP = \ @@ -190,6 +193,9 @@ QT_MOC_CPP = \ qt/voting/moc_pollwizardprojectpage.cpp \ qt/voting/moc_pollwizardsummarypage.cpp \ qt/voting/moc_pollwizardtypepage.cpp \ + qt/voting/moc_votewizard.cpp \ + qt/voting/moc_votewizardballotpage.cpp \ + qt/voting/moc_votewizardsummarypage.cpp \ qt/voting/moc_votingmodel.cpp \ qt/voting/moc_votingpage.cpp @@ -290,6 +296,9 @@ GRIDCOINRESEARCH_QT_H = \ qt/voting/pollwizardprojectpage.h \ qt/voting/pollwizardsummarypage.h \ qt/voting/pollwizardtypepage.h \ + qt/voting/votewizard.h \ + qt/voting/votewizardballotpage.h \ + qt/voting/votewizardsummarypage.h \ qt/voting/votingmodel.h \ qt/voting/votingpage.h \ qt/walletmodel.h \ @@ -370,6 +379,9 @@ GRIDCOINRESEARCH_QT_CPP = \ qt/voting/pollwizardprojectpage.cpp \ qt/voting/pollwizardsummarypage.cpp \ qt/voting/pollwizardtypepage.cpp \ + qt/voting/votewizard.cpp \ + qt/voting/votewizardballotpage.cpp \ + qt/voting/votewizardsummarypage.cpp \ qt/voting/votingmodel.cpp \ qt/voting/votingpage.cpp \ qt/walletmodel.cpp \ diff --git a/src/qt/forms/voting/votewizard.ui b/src/qt/forms/voting/votewizard.ui new file mode 100644 index 0000000000..03ff98d024 --- /dev/null +++ b/src/qt/forms/voting/votewizard.ui @@ -0,0 +1,53 @@ + + + VoteWizard + + + + 0 + 0 + 500 + 360 + + + + + 0 + 0 + + + + Vote + + + true + + + true + + + QWizard::ClassicStyle + + + QWizard::NoBackButtonOnLastPage|QWizard::NoBackButtonOnStartPage|QWizard::NoCancelButtonOnLastPage + + + + + + + VoteWizardBallotPage + QWizardPage +
voting/votewizardballotpage.h
+ 1 +
+ + VoteWizardSummaryPage + QWizardPage +
voting/votewizardsummarypage.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/voting/votewizardballotpage.ui b/src/qt/forms/voting/votewizardballotpage.ui new file mode 100644 index 0000000000..95c8a96b1c --- /dev/null +++ b/src/qt/forms/voting/votewizardballotpage.ui @@ -0,0 +1,118 @@ + + + VoteWizardBallotPage + + + + 0 + 0 + 630 + 480 + + + + + 0 + 0 + + + + + 9 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + + + + Qt::Horizontal + + + + + + + true + + + + + + + true + + + + + 0 + 0 + 596 + 392 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 9 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + PollDetails + QWidget +
voting/polldetails.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/voting/votewizardsummarypage.ui b/src/qt/forms/voting/votewizardsummarypage.ui new file mode 100644 index 0000000000..0b4d6e295d --- /dev/null +++ b/src/qt/forms/voting/votewizardsummarypage.ui @@ -0,0 +1,175 @@ + + + VoteWizardSummaryPage + + + + 0 + 0 + 630 + 480 + + + + + 0 + 0 + + + + + 9 + + + 16 + + + 16 + + + 16 + + + 16 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 60 + + + + + + + + :/icons/round_green_check + + + + + + + Vote Submitted + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Qt::AlignCenter + + + true + + + + + + + Qt::AlignCenter + + + true + + + Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 40 + + + + + + + + Your vote will tally with the next block. + + + Qt::AlignCenter + + + true + + + + + + + Qt::AlignCenter + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Copy ID + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/src/qt/voting/votewizard.cpp b/src/qt/voting/votewizard.cpp new file mode 100644 index 0000000000..fcb0869f1d --- /dev/null +++ b/src/qt/voting/votewizard.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_votewizard.h" +#include "qt/voting/votewizard.h" +#include "qt/voting/votingmodel.h" + +// ----------------------------------------------------------------------------- +// Class: VoteWizard +// ----------------------------------------------------------------------------- + +VoteWizard::VoteWizard(const PollItem& poll_item, VotingModel& voting_model, QWidget* parent) + : QWizard(parent) + , ui(new Ui::VoteWizard) +{ + ui->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose, true); + resize(GRC::ScaleSize(this, 740, 580)); + + ui->ballotPage->setModel(&voting_model); + ui->ballotPage->setPoll(poll_item); + ui->summaryPage->setPoll(poll_item); +} + +VoteWizard::~VoteWizard() +{ + delete ui; +} diff --git a/src/qt/voting/votewizard.h b/src/qt/voting/votewizard.h new file mode 100644 index 0000000000..fadd9b5498 --- /dev/null +++ b/src/qt/voting/votewizard.h @@ -0,0 +1,38 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_VOTEWIZARD_H +#define VOTING_VOTEWIZARD_H + +#include + +namespace Ui { +class VoteWizard; +} + +class PollItem; +class VotingModel; + +class VoteWizard : public QWizard +{ + Q_OBJECT + +public: + enum Pages + { + PageBallot, + PageSummary, + }; + + explicit VoteWizard( + const PollItem& poll_item, + VotingModel& voting_model, + QWidget* parent = nullptr); + ~VoteWizard(); + +private: + Ui::VoteWizard* ui; +}; + +#endif // VOTING_VOTEWIZARD_H diff --git a/src/qt/voting/votewizardballotpage.cpp b/src/qt/voting/votewizardballotpage.cpp new file mode 100644 index 0000000000..72a9f5128c --- /dev/null +++ b/src/qt/voting/votewizardballotpage.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_votewizardballotpage.h" +#include "qt/voting/votewizard.h" +#include "qt/voting/votewizardballotpage.h" +#include "qt/voting/votingmodel.h" + +#include +#include +#include + +namespace { +//! +//! \brief Provides for QWizardPage::registerField() without a real widget. +//! +struct DummyField : public QWidget +{ + DummyField(QWidget* parent = nullptr) : QWidget(parent) { hide(); } +}; +} // Anonymous namespace + +// ----------------------------------------------------------------------------- +// Class: VoteWizardBallotPage +// ----------------------------------------------------------------------------- + +VoteWizardBallotPage::VoteWizardBallotPage(QWidget *parent) + : QWizardPage(parent) + , ui(new Ui::VoteWizardBallotPage) + , m_choice_buttons(new QButtonGroup(this)) +{ + ui->setupUi(this); + + setCommitPage(true); + setButtonText(QWizard::CommitButton, tr("Submit Vote")); + + registerField("txid", new DummyField(this), "", ""); + registerField("responseLabels", new DummyField(this)); + + connect( + m_choice_buttons.get(), QOverload::of(&QButtonGroup::buttonClicked), + [this](QAbstractButton*) { emit completeChanged(); }); +} + +VoteWizardBallotPage::~VoteWizardBallotPage() +{ + delete ui; +} + +void VoteWizardBallotPage::setModel(VotingModel* voting_model) +{ + m_voting_model = voting_model; +} + +void VoteWizardBallotPage::setPoll(const PollItem& poll_item) +{ + ui->details->setItem(poll_item); + m_choice_buttons->setExclusive(!poll_item.m_multiple_choice); + m_poll_id = poll_item.m_id; + + for (const auto& choice : poll_item.m_choices) { + QAbstractButton* button; + + if (poll_item.m_multiple_choice) { + button = new QCheckBox(choice.m_label); + } else { + button = new QRadioButton(choice.m_label); + } + + ui->choicesLayout->addWidget(button); + m_choice_buttons->addButton(button); + } +} + +void VoteWizardBallotPage::initializePage() +{ + ui->errorLabel->hide(); +} + +bool VoteWizardBallotPage::validatePage() +{ + const QList choice_buttons = m_choice_buttons->buttons(); + std::vector choices; + QStringList labels; + + for (int i = 0; i < choice_buttons.count(); ++i) { + if (choice_buttons[i]->isChecked()) { + choices.emplace_back(i); + labels << choice_buttons[i]->text(); + } + } + + const VotingResult result = m_voting_model->sendVote(m_poll_id, choices); + + if (!result.ok()) { + ui->errorLabel->setText(result.error()); + ui->errorLabel->show(); + + return false; + } + + setField("txid", result.txid()); + setField("responseLabels", labels.join('\n')); + + return true; +} + +bool VoteWizardBallotPage::isComplete() const +{ + return m_choice_buttons->checkedId() != -1; +} diff --git a/src/qt/voting/votewizardballotpage.h b/src/qt/voting/votewizardballotpage.h new file mode 100644 index 0000000000..5e7201f421 --- /dev/null +++ b/src/qt/voting/votewizardballotpage.h @@ -0,0 +1,44 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_VOTEWIZARDBALLOTPAGE_H +#define VOTING_VOTEWIZARDBALLOTPAGE_H + +#include +#include + +namespace Ui { +class VoteWizardBallotPage; +} + +class PollItem; +class VotingModel; + +QT_BEGIN_NAMESPACE +class QButtonGroup; +QT_END_NAMESPACE + +class VoteWizardBallotPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit VoteWizardBallotPage(QWidget* parent = nullptr); + ~VoteWizardBallotPage(); + + void setModel(VotingModel* voting_model); + void setPoll(const PollItem& poll_item); + + void initializePage() override; + bool validatePage() override; + bool isComplete() const override; + +private: + Ui::VoteWizardBallotPage* ui; + VotingModel* m_voting_model; + std::unique_ptr m_choice_buttons; + QString m_poll_id; +}; + +#endif // VOTING_VOTEWIZARDBALLOTPAGE_H diff --git a/src/qt/voting/votewizardsummarypage.cpp b/src/qt/voting/votewizardsummarypage.cpp new file mode 100644 index 0000000000..5d39e6cb6e --- /dev/null +++ b/src/qt/voting/votewizardsummarypage.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "qt/decoration.h" +#include "qt/forms/voting/ui_votewizardsummarypage.h" +#include "qt/voting/votewizardsummarypage.h" +#include "qt/voting/votingmodel.h" + +#include + +// ----------------------------------------------------------------------------- +// Class: VoteWizardSummaryPage +// ----------------------------------------------------------------------------- + +VoteWizardSummaryPage::VoteWizardSummaryPage(QWidget* parent) + : QWizardPage(parent) + , ui(new Ui::VoteWizardSummaryPage) +{ + ui->setupUi(this); + + GRC::ScaleFontPointSize(ui->pageTitleLabel, 14); + GRC::ScaleFontPointSize(ui->pollTitleLabel, 12); + GRC::ScaleFontPointSize(ui->voteIdLabel, 8); +} + +VoteWizardSummaryPage::~VoteWizardSummaryPage() +{ + delete ui; +} + +void VoteWizardSummaryPage::setPoll(const PollItem& poll_item) +{ + ui->pollTitleLabel->setText(poll_item.m_title); +} + +void VoteWizardSummaryPage::initializePage() +{ + ui->responsesLabel->setText(field("responseLabels").toString()); + ui->voteIdLabel->setText(field("txid").toString()); +} + +void VoteWizardSummaryPage::on_copyToClipboardButton_clicked() const +{ + QApplication::clipboard()->setText(field("txid").toString()); +} diff --git a/src/qt/voting/votewizardsummarypage.h b/src/qt/voting/votewizardsummarypage.h new file mode 100644 index 0000000000..92e855f320 --- /dev/null +++ b/src/qt/voting/votewizardsummarypage.h @@ -0,0 +1,35 @@ +// Copyright (c) 2014-2021 The Gridcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef VOTING_VOTEWIZARDSUMMARYPAGE_H +#define VOTING_VOTEWIZARDSUMMARYPAGE_H + +#include + +namespace Ui { +class VoteWizardSummaryPage; +} + +class PollItem; + +class VoteWizardSummaryPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit VoteWizardSummaryPage(QWidget* parent = nullptr); + ~VoteWizardSummaryPage(); + + void setPoll(const PollItem& poll_item); + + void initializePage() override; + +private: + Ui::VoteWizardSummaryPage* ui; + +private slots: + void on_copyToClipboardButton_clicked() const; +}; + +#endif // VOTING_VOTEWIZARDSUMMARYPAGE_H From 0d548a5d0cf94dc3a370d1f8b0e802ddbe42d1d3 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:52 -0500 Subject: [PATCH 171/280] Remove Qt Charts dependency This removes the dependency for the Qt Charts submodule and autotools pieces that check for it. Gridcoin used Qt Charts for the voting GUI. The redesigned UI displays poll results with standard widgets now, so we don't need the charting library. --- build-aux/m4/bitcoin_qt.m4 | 7 ------- ci/test/00_setup_env_native.sh | 2 +- depends/packages/qt.mk | 12 ------------ depends/patches/qt/subdirs.pro | 4 ---- doc/build-unix.md | 10 +++------- gridcoinresearch.pro | 6 ------ 6 files changed, 4 insertions(+), 37 deletions(-) diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index d9a73d7865..96be384bc2 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -372,8 +372,6 @@ AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [ PKG_CHECK_MODULES([QT_THEME], [${qt_lib_prefix}ThemeSupport${qt_lib_suffix}], [QT_LIBS="$QT_THEME_LIBS $QT_LIBS"]) dnl Gridcoin uses Concurrent: PKG_CHECK_MODULES([QT_CONCURRENT], [${qt_lib_prefix}Concurrent${qt_lib_suffix}], [QT_LIBS="$QT_CONCURRENT_LIBS $QT_LIBS"]) - dnl Gridcoin uses Charts: - PKG_CHECK_MODULES([QT_CHARTS], [${qt_lib_prefix}Charts${qt_lib_suffix}], [QT_LIBS="$QT_CONCURRENT_LIBS $QT_LIBS"]) dnl Gridcoin uses SVG: PKG_CHECK_MODULES([QT_SVG], [${qt_lib_prefix}Svg${qt_lib_suffix}], [QT_LIBS="$QT_SVG_LIBS $QT_LIBS"]) if test "x$TARGET_OS" = xlinux; then @@ -420,11 +418,6 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS],[ PKG_CHECK_MODULES([QT_CONCURRENT], [${qt_lib_prefix}Concurrent${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CONCURRENT_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CONCURRENT_LIBS $QT_LIBS"], [BITCOIN_QT_FAIL([${qt_lib_prefix}Concurrent${qt_lib_suffix} $qt_version not found])]) ]) - dnl Gridcoin uses Charts: - BITCOIN_QT_CHECK([ - PKG_CHECK_MODULES([QT_CHARTS], [${qt_lib_prefix}Charts${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CHARTS_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CHARTS_LIBS $QT_LIBS" CPPFLAGS="$CPPFLAGS -DQT_CHARTS_LIB"], - [AC_MSG_WARN([${qt_lib_prefix}Charts${qt_lib_suffix} $qt_version not found. Poll results will not display charts.])]) - ]) BITCOIN_QT_CHECK([ PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test${qt_lib_suffix} $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no]) diff --git a/ci/test/00_setup_env_native.sh b/ci/test/00_setup_env_native.sh index 0eefc5d247..cd40b39c9f 100755 --- a/ci/test/00_setup_env_native.sh +++ b/ci/test/00_setup_env_native.sh @@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native export DOCKER_NAME_TAG=ubuntu:20.04 -export PACKAGES="libqt5gui5 libqt5core5a qtbase5-dev libqt5dbus5 qttools5-dev qttools5-dev-tools libqt5charts5-dev libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-iostreams-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libqrencode-dev libzip-dev zlib1g zlib1g-dev libcurl4 libcurl4-openssl-dev" +export PACKAGES="libqt5gui5 libqt5core5a qtbase5-dev libqt5dbus5 qttools5-dev qttools5-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-iostreams-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libqrencode-dev libzip-dev zlib1g zlib1g-dev libcurl4 libcurl4-openssl-dev" export RUN_UNIT_TESTS=true # export RUN_FUNCTIONAL_TESTS=false # export RUN_SECURITY_TESTS="true" diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index df8075d0e4..ed39c56691 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -18,17 +18,12 @@ $(package)_qttranslations_sha256_hash=577b0668a777eb2b451c61e8d026d79285371597ce $(package)_qttools_file_name=qttools-$($(package)_suffix) $(package)_qttools_sha256_hash=98b2aaca230458f65996f3534fd471d2ffd038dd58ac997c0589c06dc2385b4f -# Gridcoin uses charts for the voting features: -$(package)_qtcharts_file_name=qtcharts-$($(package)_suffix) -$(package)_qtcharts_sha256_hash=8567502335913a45dbe47c5b493974b48c2049dc07ab5a2a273ddfdcf43c002c - # Gridcoin displays SVG images in the GUI: $(package)_qtsvg_file_name=qtsvg-$($(package)_suffix) $(package)_qtsvg_sha256_hash=7a6857a2f68cfbebb9f791396b401a98e951c9bff9bfeb1b5b01914c3ea1a0ed $(package)_extra_sources = $($(package)_qttranslations_file_name) $(package)_extra_sources += $($(package)_qttools_file_name) -$(package)_extra_sources += $($(package)_qtcharts_file_name) $(package)_extra_sources += $($(package)_qtsvg) define $(package)_set_vars @@ -196,7 +191,6 @@ define $(package)_fetch_cmds $(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttranslations_file_name),$($(package)_qttranslations_file_name),$($(package)_qttranslations_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttools_file_name),$($(package)_qttools_file_name),$($(package)_qttools_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qtcharts_file_name),$($(package)_qtcharts_file_name),$($(package)_qtcharts_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_qtsvg_file_name),$($(package)_qtsvg_file_name),$($(package)_qtsvg_sha256_hash)) endef @@ -205,7 +199,6 @@ define $(package)_extract_cmds echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ echo "$($(package)_qttranslations_sha256_hash) $($(package)_source_dir)/$($(package)_qttranslations_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ echo "$($(package)_qttools_sha256_hash) $($(package)_source_dir)/$($(package)_qttools_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_qtcharts_sha256_hash) $($(package)_source_dir)/$($(package)_qtcharts_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ echo "$($(package)_qtsvg_sha256_hash) $($(package)_source_dir)/$($(package)_qtsvg_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ mkdir qtbase && \ @@ -214,8 +207,6 @@ define $(package)_extract_cmds tar --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \ mkdir qttools && \ tar --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools && \ - mkdir qtcharts && \ - tar --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qtcharts_file_name) -C qtcharts && \ mkdir qtsvg && \ tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qtsvg_file_name) -C qtsvg endef @@ -288,7 +279,6 @@ define $(package)_config_cmds qtbase/bin/qmake -o qttools/src/linguist/lrelease/Makefile qttools/src/linguist/lrelease/lrelease.pro && \ qtbase/bin/qmake -o qttools/src/linguist/lupdate/Makefile qttools/src/linguist/lupdate/lupdate.pro && \ qtbase/bin/qmake -o qttools/src/linguist/lconvert/Makefile qttools/src/linguist/lconvert/lconvert.pro && \ - qtbase/bin/qmake -o qtcharts/src/charts/Makefile qtcharts/src/charts/charts.pro && \ qtbase/bin/qmake -o qtsvg/src/Makefile qtsvg/src/src.pro endef @@ -299,7 +289,6 @@ define $(package)_build_cmds $(MAKE) -C qttools/src/linguist/lupdate && \ $(MAKE) -C qttools/src/linguist/lconvert && \ $(MAKE) -C qttranslations && \ - $(MAKE) -C qtcharts/src/charts && \ $(MAKE) -C qtsvg/src \ ' endef @@ -311,7 +300,6 @@ define $(package)_stage_cmds $(MAKE) -C qttools/src/linguist/lupdate INSTALL_ROOT=$($(package)_staging_dir) install_target && \ $(MAKE) -C qttools/src/linguist/lconvert INSTALL_ROOT=$($(package)_staging_dir) install_target && \ $(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets && \ - $(MAKE) -C qtcharts/src/charts INSTALL_ROOT=$($(package)_staging_dir) install && \ $(MAKE) -C qtsvg/src INSTALL_ROOT=$($(package)_staging_dir) install \ ' endef diff --git a/depends/patches/qt/subdirs.pro b/depends/patches/qt/subdirs.pro index f2ab21932d..2c69c0cbae 100644 --- a/depends/patches/qt/subdirs.pro +++ b/depends/patches/qt/subdirs.pro @@ -11,16 +11,12 @@ TEMPLATE = subdirs SUBDIRS = \ qtbase \ - qtcharts \ qtsvg \ qttools \ qttranslations qtbase.target = module-qtbase -qtcharts.target = module-qtcharts -qtcharts.depends = qtbase - qtsvg.target = module-qtsvg qtsvg.depends = qtbase diff --git a/doc/build-unix.md b/doc/build-unix.md index 662eac7c0d..2a0fa27465 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -135,7 +135,7 @@ To build without GUI pass `--without-gui` to configure. To build with Qt 5 (recommended) you need the following: - sudo apt-get install libqt5gui5 libqt5core5a libqt5charts5-dev libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler + sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler libqrencode (enabled by default, switch off by passing `--without-qrencode` to configure) can be installed with: @@ -188,11 +188,7 @@ To build without GUI pass `--without-gui` to configure. To build with Qt 5 (recommended) you need the following: - sudo zypper install libQt5Gui5 libQt5Core5 libQt5Charts5 libQt5DBus5 libQt5Network-devel libqt5-qttools-devel libqt5-qttools - -Additionally for Tumbleweed: - - sudo zypper install libQt5Charts5-designer + sudo zypper install libQt5Gui5 libQt5Core5 libQt5DBus5 libQt5Network-devel libqt5-qttools-devel libqt5-qttools libqrencode (enabled by default, switch off by passing `--without-qrencode` to configure) can be installed with: @@ -222,7 +218,7 @@ Dependencies for the GUI: Alpine Linux To build the Qt GUI on Alpine Linux, we need these dependencies: - apk add libqrencode-dev protobuf-dev qt5-qtbase-dev qt5-qtcharts-dev qt5-qtsvg-dev qt5-qttools-dev + apk add libqrencode-dev protobuf-dev qt5-qtbase-dev qt5-qtsvg-dev qt5-qttools-dev Setup and Build Example: Arch Linux diff --git a/gridcoinresearch.pro b/gridcoinresearch.pro index 5158676698..b73dde67c7 100755 --- a/gridcoinresearch.pro +++ b/gridcoinresearch.pro @@ -15,12 +15,6 @@ win32 { DEFINES += _WIN32_WINNT=0x0501 WINVER=0x0501 } -lessThan(QT_MAJOR_VERSION, 5) | lessThan(QT_MINOR_VERSION, 8) { - # Qt charts not available -}else{ - QT += charts -} - # for boost 1.37, add -mt to the boost libraries # use: qmake BOOST_LIB_SUFFIX=-mt # for boost thread win32 with _win32 sufix From 74de3ed8f68aded8d19ddb456c233737d5b4d17f Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Mon, 31 May 2021 19:46:55 -0500 Subject: [PATCH 172/280] Add system tray notification for new polls This adds a GUI hook that displays a system notification when a node receives a new poll as well as an accompanying GUI option to disable those notifications. --- src/qt/bitcoingui.cpp | 21 +++++++++++++++++++++ src/qt/bitcoingui.h | 1 + src/qt/forms/optionsdialog.ui | 7 +++++++ src/qt/optionsdialog.cpp | 1 + src/qt/optionsmodel.cpp | 12 ++++++++++++ src/qt/optionsmodel.h | 3 +++ 6 files changed, 45 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f2471c43b6..d6776852cc 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -42,6 +42,7 @@ #include "clicklabel.h" #include "univalue.h" #include "upgradeqt.h" +#include "voting/votingmodel.h" #ifdef Q_OS_MAC #include "macdockiconhandler.h" @@ -807,6 +808,12 @@ void BitcoinGUI::setResearcherModel(ResearcherModel *researcherModel) void BitcoinGUI::setVotingModel(VotingModel *votingModel) { votingPage->setVotingModel(votingModel); + + if (!votingModel) { + return; + } + + connect(votingModel, SIGNAL(newPollReceived()), this, SLOT(handleNewPoll())); } void BitcoinGUI::createTrayIcon() @@ -1786,6 +1793,20 @@ void BitcoinGUI::updateBeaconIcon() .arg(researcherModel->formatBeaconStatus())); } +void BitcoinGUI::handleNewPoll() +{ + if (!clientModel || !clientModel->getOptionsModel()) { + return; + } + + if (!clientModel->getOptionsModel()->getDisablePollNotifications()) { + notificator->notify( + Notificator::Information, + tr("New Poll"), + tr("A new poll is available. Open Gridcoin to vote.")); + } +} + // ----------------------------------------------------------------------------- // Class: ToolbarButtonIconFilter // ----------------------------------------------------------------------------- diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 7ef1163cc8..3f6d7bddf7 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -274,6 +274,7 @@ private slots: QString GetEstimatedStakingFrequency(unsigned int nEstimateTime); void updateGlobalStatus(); + void handleNewPoll(); }; //! diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 2f24fb4e88..43bbb533f0 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -286,6 +286,13 @@ + + + + Disable Poll Notifications + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 45749d48ba..2d85a2a12f 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -148,6 +148,7 @@ void OptionsDialog::setMapper() /* Window */ mapper->addMapping(ui->disableTransactionNotifications, OptionsModel::DisableTrxNotifications); + mapper->addMapping(ui->disablePollNotifications, OptionsModel::DisablePollNotifications); #ifndef Q_OS_MAC mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 7219246236..a8014f2db8 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -46,6 +46,7 @@ void OptionsModel::Init() fStartMin = settings.value("fStartMin", true).toBool(); fMinimizeToTray = settings.value("fMinimizeToTray", false).toBool(); fDisableTrxNotifications = settings.value("fDisableTrxNotifications", false).toBool(); + fDisablePollNotifications = settings.value("fDisablePollNotifications", false).toBool(); bDisplayAddresses = settings.value("bDisplayAddresses", false).toBool(); fMinimizeOnClose = settings.value("fMinimizeOnClose", false).toBool(); fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); @@ -97,6 +98,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(fMinimizeToTray); case DisableTrxNotifications: return QVariant(fDisableTrxNotifications); + case DisablePollNotifications: + return QVariant(fDisablePollNotifications); case MapPortUPnP: return settings.value("fUseUPnP", gArgs.GetBoolArg("-upnp", true)); case MinimizeOnClose: @@ -178,6 +181,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in fDisableTrxNotifications = value.toBool(); settings.setValue("fDisableTrxNotifications", fDisableTrxNotifications); break; + case DisablePollNotifications: + fDisablePollNotifications = value.toBool(); + settings.setValue("fDisablePollNotifications", fDisablePollNotifications); + break; case MapPortUPnP: fUseUPnP = value.toBool(); settings.setValue("fUseUPnP", fUseUPnP); @@ -338,6 +345,11 @@ bool OptionsModel::getDisableTrxNotifications() return fDisableTrxNotifications; } +bool OptionsModel::getDisablePollNotifications() +{ + return fDisablePollNotifications; +} + bool OptionsModel::getMinimizeOnClose() { return fMinimizeOnClose; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 14ed35f5fb..6d77b0a794 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -22,6 +22,7 @@ class OptionsModel : public QAbstractListModel MinimizeToTray, // bool StartMin, // bool DisableTrxNotifications, // bool + DisablePollNotifications,// bool MapPortUPnP, // bool MinimizeOnClose, // bool ProxyUse, // bool @@ -54,6 +55,7 @@ class OptionsModel : public QAbstractListModel bool getStartMin(); bool getMinimizeToTray(); bool getDisableTrxNotifications(); + bool getDisablePollNotifications(); bool getMinimizeOnClose(); int getDisplayUnit(); bool getDisplayAddresses(); @@ -75,6 +77,7 @@ class OptionsModel : public QAbstractListModel bool fStartAtStartup; bool fStartMin; bool fDisableTrxNotifications; + bool fDisablePollNotifications; bool bDisplayAddresses; bool fMinimizeOnClose; bool fCoinControlFeatures; From 2d895f8633ea9be008be2df399d79ea9dfaa6a7f Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Wed, 2 Jun 2021 15:37:04 -0500 Subject: [PATCH 173/280] Fix GUI Inter font rendering on Windows with FreeType This switches the font rendering engine for Windows to Qt's built-in FreeType engine and changes the Windows font family from Segoe UI to Inter. On Windows, Qt rendered the OpenType font poorly--text looked blurry and distorted. A temporary choice was made to use the system- provided Segoe UI typeface instead. Here, we configure the Qt depends recipe to include FreeType support on Windows and activate that feature when the application starts. We could switch the format of the Inter font to TrueType, but we'd need to build it from source since the Inter project doesn't distribute a desktop TTF set, and the OpenType's "weightier" appearance is easier to read on a standard-DPI display. FreeType renders non-Latin glyphs more smoothly on high-DPI desktops as well. --- depends/packages/qt.mk | 1 + src/qt/bitcoin.cpp | 14 ++++++++++++++ src/qt/res/stylesheets/dark_stylesheet.qss | 2 +- src/qt/res/stylesheets/light_stylesheet.qss | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index df8075d0e4..df1e0b6449 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -166,6 +166,7 @@ $(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cflags) $($(pa $(package)_config_opts_mingw32 += "QMAKE_LFLAGS = '$($(package)_ldflags)'" $(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_mingw32 += -pch +$(package)_config_opts_mingw32 += -qt-freetype $(package)_config_opts_android = -xplatform android-clang $(package)_config_opts_android += -android-sdk $(ANDROID_SDK) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bb4a853328..94acd07ce5 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -278,6 +278,20 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } +#ifdef Q_OS_WIN + // Use Qt's built-in FreeType rendering engine to display text on Windows. + // We use the Inter font's OpenType format which doesn't render clearly on + // Windows in Qt applications with the default engine. The TrueType format + // works fine in either case, but the OpenType appearance is more legible. + // Apply this before instantiating QApplication. This environment variable + // configures the option for Qt's Windows integration plugin which doesn't + // have a C++ API. + // + if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { + qputenv("QT_QPA_PLATFORM", "windows:fontengine=freetype"); + } +#endif + // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #if QT_VERSION >= 0x050600 diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index 91e7f4ea32..963ab1f157 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -7,7 +7,7 @@ QWidget { color: rgb(204, 208, 209); - font-family: "SF Pro Display", "Segoe UI", "Inter"; + font-family: "SF Pro Display", "Inter"; } QMainWindow { diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index aa269e8709..f2841ab45a 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -7,7 +7,7 @@ QWidget { color: rgb(58, 70, 93); - font-family: "SF Pro Display", "Segoe UI", "Inter"; + font-family: "SF Pro Display", "Inter"; } QMainWindow { From 6d09495e41ce6a9dfb39c50498219eebe01a09e2 Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Wed, 9 Jun 2021 18:17:34 -0500 Subject: [PATCH 174/280] Refresh GUI checkbox and radio button styles This updates the appearance of checkbox and radio button controls to match the proposed design mock ups. --- src/Makefile.qt.include | 4 ++ src/qt/bitcoin.qrc | 4 ++ src/qt/res/icons/checkbox_checked.svg | 1 + src/qt/res/icons/checkbox_indeterminate.svg | 1 + src/qt/res/icons/checkbox_unchecked.svg | 1 + src/qt/res/icons/radio_checked.svg | 2 + src/qt/res/stylesheets/dark_stylesheet.qss | 57 +++++++++++++++++++++ src/qt/res/stylesheets/light_stylesheet.qss | 57 +++++++++++++++++++++ 8 files changed, 127 insertions(+) create mode 100644 src/qt/res/icons/checkbox_checked.svg create mode 100644 src/qt/res/icons/checkbox_indeterminate.svg create mode 100644 src/qt/res/icons/checkbox_unchecked.svg create mode 100644 src/qt/res/icons/radio_checked.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 5da3c14f58..8d359709fd 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -396,6 +396,9 @@ RES_ICONS = \ qt/res/icons/clock3.png \ qt/res/icons/clock4.png \ qt/res/icons/clock5.png \ + qt/res/icons/checkbox_checked.svg \ + qt/res/icons/checkbox_indeterminate.svg \ + qt/res/icons/checkbox_unchecked.svg \ qt/res/icons/configure.png \ qt/res/icons/connect0.svg \ qt/res/icons/connect1.svg \ @@ -423,6 +426,7 @@ RES_ICONS = \ qt/res/icons/open_link.svg \ qt/res/icons/qrcode.png \ qt/res/icons/quit.png \ + qt/res/icons/radio_checked.svg \ qt/res/icons/remove.png \ qt/res/icons/round_gray_x.svg \ qt/res/icons/round_green_check.svg \ diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 1e9e62e37e..0675abe7ee 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -201,6 +201,10 @@ res/icons/light_mode_active.svg res/icons/no_result.svg res/icons/open_link.svg + res/icons/checkbox_checked.svg + res/icons/checkbox_indeterminate.svg + res/icons/checkbox_unchecked.svg + res/icons/radio_checked.svg
diff --git a/src/qt/res/icons/checkbox_checked.svg b/src/qt/res/icons/checkbox_checked.svg new file mode 100644 index 0000000000..2e94c5ad90 --- /dev/null +++ b/src/qt/res/icons/checkbox_checked.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/checkbox_indeterminate.svg b/src/qt/res/icons/checkbox_indeterminate.svg new file mode 100644 index 0000000000..e9666f3909 --- /dev/null +++ b/src/qt/res/icons/checkbox_indeterminate.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/checkbox_unchecked.svg b/src/qt/res/icons/checkbox_unchecked.svg new file mode 100644 index 0000000000..cd81d1b918 --- /dev/null +++ b/src/qt/res/icons/checkbox_unchecked.svg @@ -0,0 +1 @@ + diff --git a/src/qt/res/icons/radio_checked.svg b/src/qt/res/icons/radio_checked.svg new file mode 100644 index 0000000000..609f33a5b4 --- /dev/null +++ b/src/qt/res/icons/radio_checked.svg @@ -0,0 +1,2 @@ + + diff --git a/src/qt/res/stylesheets/dark_stylesheet.qss b/src/qt/res/stylesheets/dark_stylesheet.qss index cf00407ff8..88591eda0c 100644 --- a/src/qt/res/stylesheets/dark_stylesheet.qss +++ b/src/qt/res/stylesheets/dark_stylesheet.qss @@ -438,6 +438,63 @@ QSpinBox::down-arrow { image: url(:/icons/dark_chevron_down); } +QCheckBox::indicator, +QRadioButton::indicator, +QTreeView::indicator { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(234, 237, 237), stop: 1 rgb(202, 210, 210)); + border: 0.13em solid rgb(170, 182, 182); + border-radius: 0.26em; +} + +QCheckBox::indicator, +QRadioButton::indicator { + width: 0.8em; + height: 0.8em; +} + +QRadioButton::indicator { + border-radius: 0.5em; +} + +QCheckBox::indicator:unchecked, +QRadioButton::indicator:unchecked, +QTreeView::indicator:unchecked { + image: url(:/icons/checkbox_unchecked); +} + +QCheckBox::indicator:unchecked:hover, +QRadioButton::indicator:unchecked:hover, +QTreeView::indicator:unchecked:hover { + border-color: rgb(33, 114, 208); +} + +QCheckBox::indicator:checked, +QCheckBox::indicator:indeterminate, +QRadioButton::indicator:checked, +QTreeView::indicator:checked, +QTreeView::indicator:indeterminate { + background: rgb(74, 144, 226); + border-color: rgb(74, 144, 226); + image: url(:/icons/checkbox_checked); +} + +QCheckBox::indicator:indeterminate, +QTreeView::indicator:indeterminate { + image: url(:/icons/checkbox_indeterminate); +} + +QCheckBox::indicator:checked:hover, +QCheckBox::indicator:indeterminate:hover, +QTreeView::indicator:checked:hover, +QTreeView::indicator:indeterminate:hover { + background: rgb(33, 114, 208); + border-color: rgb(33, 114, 208); +} + +QRadioButton::indicator::checked { + image: url(:/icons/radio_checked); +} + QTabWidget::tab-bar { left: 0.5em; } diff --git a/src/qt/res/stylesheets/light_stylesheet.qss b/src/qt/res/stylesheets/light_stylesheet.qss index 270ce2387b..00585b608d 100644 --- a/src/qt/res/stylesheets/light_stylesheet.qss +++ b/src/qt/res/stylesheets/light_stylesheet.qss @@ -429,6 +429,63 @@ QSpinBox::down-arrow { image: url(:/icons/light_chevron_down); } +QCheckBox::indicator, +QRadioButton::indicator, +QTreeView::indicator { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 white, stop: 1 rgb(234, 237, 237)); + border: 0.13em solid rgb(191, 199, 215); + border-radius: 0.26em; +} + +QCheckBox::indicator, +QRadioButton::indicator { + width: 0.8em; + height: 0.8em; +} + +QRadioButton::indicator { + border-radius: 0.5em; +} + +QCheckBox::indicator:unchecked, +QRadioButton::indicator:unchecked, +QTreeView::indicator:unchecked { + image: url(:/icons/checkbox_unchecked); +} + +QCheckBox::indicator:unchecked:hover, +QRadioButton::indicator:unchecked:hover, +QTreeView::indicator:unchecked:hover { + border-color: rgb(144, 19, 254); +} + +QCheckBox::indicator:checked, +QCheckBox::indicator:indeterminate, +QRadioButton::indicator:checked, +QTreeView::indicator:checked, +QTreeView::indicator:indeterminate { + background: rgb(172, 78, 254); + border-color: rgb(172, 78, 254); + image: url(:/icons/checkbox_checked); +} + +QCheckBox::indicator:indeterminate, +QTreeView::indicator:indeterminate { + image: url(:/icons/checkbox_indeterminate); +} + +QCheckBox::indicator:checked:hover, +QCheckBox::indicator:indeterminate:hover, +QTreeView::indicator:checked:hover, +QTreeView::indicator:indeterminate:hover { + background: rgb(144, 19, 254); + border-color: rgb(144, 19, 254); +} + +QRadioButton::indicator::checked { + image: url(:/icons/radio_checked); +} + QTabWidget::tab-bar { left: 0.5em; } From 61d30f4bada6df9dec26cc0d0fd7b48019fb9236 Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 10 Jun 2021 16:26:59 -0400 Subject: [PATCH 175/280] Bump libevent to 2.1.11 Ref https://github.com/bitcoin/bitcoin/pull/17008 --- depends/packages/libevent.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index ab25472b9b..8879dd9467 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,9 +1,9 @@ package=libevent GCCFLAGS?= -$(package)_version=2.1.8-stable +$(package)_version=2.1.11-stable $(package)_download_path=https://github.com/libevent/libevent/archive/ $(package)_file_name=release-$($(package)_version).tar.gz -$(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d +$(package)_sha256_hash=229393ab2bf0dc94694f21836846b424f3532585bac3468738b7bf752c03901e define $(package)_preprocess_cmds ./autogen.sh From 057dce715b23b76c12f32f8af0407571b6a16d2c Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 10 Jun 2021 16:47:34 -0400 Subject: [PATCH 176/280] Remove bigsur patch I missed with QT update --- depends/packages/qt.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 3e29255757..94057808aa 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -10,7 +10,7 @@ $(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no $(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch $(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch $(package)_patches+= fix_lib_paths.patch fix_android_pch.patch -$(package)_patches+= fix_bigsur_drawing.patch qtbase-moc-ignore-gcc-macro.patch subdirs.pro +$(package)_patches+= qtbase-moc-ignore-gcc-macro.patch subdirs.pro $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=577b0668a777eb2b451c61e8d026d79285371597ce9df06b6dee6c814164b7c3 From 7e42171ca1ad3c17b36d0cf99e54a01f679d2e28 Mon Sep 17 00:00:00 2001 From: barton26 Date: Thu, 10 Jun 2021 16:48:46 -0400 Subject: [PATCH 177/280] Update native_mac_alias Fixes a bug. Refer https://github.com/bitcoin/bitcoin/pull/21658 --- depends/packages/native_mac_alias.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index 5fe027fb8a..783f87ca7c 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -1,8 +1,8 @@ package=native_mac_alias -$(package)_version=2.1.1 +$(package)_version=2.2.0 $(package)_download_path=https://github.com/al45tair/mac_alias/archive/ $(package)_file_name=v$($(package)_version).tar.gz -$(package)_sha256_hash=c0ffceee14f7d04a6eb323fb7b8217dc3f373b346198d2ca42300a8362db7efa +$(package)_sha256_hash=421e6d7586d1f155c7db3e7da01ca0dacc9649a509a253ad7077b70174426499 $(package)_install_libdir=$(build_prefix)/lib/python3/dist-packages define $(package)_build_cmds From a1c8970cb84fbdbc319a5feb7470b878aeeee665 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Thu, 10 Jun 2021 19:02:05 -0400 Subject: [PATCH 178/280] Fix assert on non-existent data directory This is a fix for a regression that happened from porting the ArgsManager class. --- src/qt/bitcoin.cpp | 21 ++++++++++----------- src/util/system.cpp | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 99e6884130..bb58c9c038 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -277,17 +277,6 @@ int main(int argc, char *argv[]) /** Check mainnet config file first in case testnet is set there and not in command line args **/ SelectParams(CBaseChainParams::MAIN); - // Currently unused. - std::string error_msg; - - if (!gArgs.ReadConfigFiles(error_msg, true)) { - ThreadSafeMessageBox(strprintf("Error reading configuration file.\n"), - "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); - QMessageBox::critical(nullptr, PACKAGE_NAME, - QObject::tr("Error: Cannot parse configuration file.")); - return EXIT_FAILURE; - } - #ifdef Q_OS_WIN // Use Qt's built-in FreeType rendering engine to display text on Windows. // We use the Inter font's OpenType format which doesn't render clearly on @@ -315,6 +304,16 @@ int main(int argc, char *argv[]) RegisterMetaTypes(); QApplication app(argc, argv); + // Not currently useful. + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) { + ThreadSafeMessageBox(strprintf("Error reading configuration file.\n"), + "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); + QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Cannot parse configuration file.")); + return EXIT_FAILURE; + } + #if defined(WIN32) && defined(QT_GUI) SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #endif diff --git a/src/util/system.cpp b/src/util/system.cpp index bbacfb38f0..a74c77970d 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -712,7 +712,14 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) if (path.is_absolute()) { return path; } - return fsbridge::AbsPathJoin(GetDataDir(net_specific), path); + + fs::path data_dir = GetDataDir(net_specific); + + if (data_dir.empty()) { + return fs::path {}; + } else { + return fsbridge::AbsPathJoin(data_dir, path); + } } fs::path GetDefaultDataDir() @@ -869,7 +876,10 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) const std::string confPath = GetArg("-conf", GRIDCOIN_CONF_FILENAME); - fsbridge::ifstream stream(GetConfigFile(confPath)); + fs::path config_file_spec = GetConfigFile(confPath); + if (config_file_spec.empty()) return false; + + fsbridge::ifstream stream(config_file_spec); // ok to not have a config file if (stream.good()) { From 3549e0f78bdd197913c57b6588cc6724f34ea420 Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:39:11 +0000 Subject: [PATCH 179/280] Remove comma --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f6110705de..44c77159dc 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: Gridcoin Discord url: https://discord.gg/jf9XX4a - about: Please go here if you have any general issues, that aren't bug reports. We can assist you much faster there. + about: Please go here if you have any general issues that aren't bug reports. We can assist you much faster there. - name: Gridcoin Subreddit url: https://reddit.com/r/gridcoin about: Alternative platform. From 79d1892bd92bf0e178d65f0abdb693112a9a90be Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:41:58 +0000 Subject: [PATCH 180/280] change order of Github and StackBlitz --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 568e0c196e..e2d9373ab0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -22,7 +22,7 @@ labels: enhancement **Related Code** - + **Additional Context** From 8533e03e57862a5ce78c032b4a8a59c8d02441d3 Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:42:46 +0000 Subject: [PATCH 181/280] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e2d9373ab0..48b340e4db 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -25,4 +25,4 @@ labels: enhancement **Additional Context** - + From b2546c775b3fcd5d890dbe19edcbfe592bbafb25 Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:11:03 +0000 Subject: [PATCH 182/280] aren't -> are not --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 44c77159dc..68151bb779 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: Gridcoin Discord url: https://discord.gg/jf9XX4a - about: Please go here if you have any general issues that aren't bug reports. We can assist you much faster there. + about: Please go here if you have any general issues that are not bug reports. We can assist you much faster there. - name: Gridcoin Subreddit url: https://reddit.com/r/gridcoin about: Alternative platform. From 4152b033c050ef82b3a8dae47e1e31ef5a522e4b Mon Sep 17 00:00:00 2001 From: barton26 Date: Sat, 12 Jun 2021 02:25:57 -0400 Subject: [PATCH 183/280] Remove CCT it's dead :( --- doc/README.md | 1 - doc/release-process.md | 2 -- 2 files changed, 3 deletions(-) diff --git a/doc/README.md b/doc/README.md index 3dc1bfe585..9e14d3b510 100644 --- a/doc/README.md +++ b/doc/README.md @@ -16,7 +16,6 @@ To download Gridcoin, visit [gridcoin.us](https://gridcoin.us). for help and more information. * A lot of features core features are based on Bitcoin and have been documented on the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) * Ask for help or discuss on [#gridcoin](https://webchat.freenode.net?channels=gridcoin) on Freenode. -* Ask for help or discuss on the [Cryptocurrencytalk](https://cryptocurrencytalk.com/forum/464-gridcoin-grc/) forums * You can also join us on [Slack](https://grcinvite.herokuapp.com/) with the invite Token GRCsquad Building diff --git a/doc/release-process.md b/doc/release-process.md index d6cd70ab69..fe8e82ffad 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -243,8 +243,6 @@ rm SHA256SUMS - Announce the release: - - Cryptocurrencytalk post - - Update title of #gridcoin on Freenode IRC - Twitter, reddit /r/Gridcoin and Slack From f77cd931e65e252c637b005fca874bb57115b520 Mon Sep 17 00:00:00 2001 From: barton26 Date: Sat, 12 Jun 2021 02:49:15 -0400 Subject: [PATCH 184/280] Misc Grammar Remove extraneous "the" Remove extraneous "to" Remove extraneous "is" Remove extraneous "of" Several cases of "a" to "an" IP should be capitalized in docs --- doc/gridcoinresearch.conf.md | 2 +- src/amount.h | 2 +- src/gridcoin/accrual/snapshot.h | 2 +- src/gridcoin/beacon.cpp | 2 +- src/gridcoin/researcher.cpp | 4 ++-- src/gridcoin/scraper/fwd.h | 2 +- src/gridcoin/scraper/scraper.cpp | 4 ++-- src/gridcoin/scraper/scraper_net.h | 2 +- src/gridcoin/voting/registry.cpp | 2 +- src/qt/walletmodel.cpp | 2 +- src/univalue/lib/univalue_get.cpp | 4 ++-- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/gridcoinresearch.conf.md b/doc/gridcoinresearch.conf.md index aab08c35df..32dbb93fa5 100644 --- a/doc/gridcoinresearch.conf.md +++ b/doc/gridcoinresearch.conf.md @@ -117,7 +117,7 @@ It is not required for all of the addnodes to be reachable, but at least one reliable addnode, and preferably more than one that is reachable, should be in your config file. -Ensure you don't have an addnode=your own ip, or you will end up banning +Ensure you don't have an addnode=your own IP, or you will end up banning yourself (because when the node sends itself the first message, the local time is far enough off of the network time (which it does not know yet) so it will ban itself. diff --git a/src/amount.h b/src/amount.h index 81415f981f..c5d361d5b2 100644 --- a/src/amount.h +++ b/src/amount.h @@ -20,7 +20,7 @@ static const CAmount CENT = 1000000; * currently happens to be less than 2,000,000,000 GRC for various reasons, but * rather a sanity check. As this sanity check is used by consensus-critical * validation code, the exact value of the MAX_MONEY constant is consensus - * critical; in unusual circumstances like a overflow bug that allowed + * critical; in unusual circumstances like an overflow bug that allowed * for the creation of coins out of thin air modification could lead to a fork. * */ static const CAmount MAX_MONEY = 2000000000 * COIN; diff --git a/src/gridcoin/accrual/snapshot.h b/src/gridcoin/accrual/snapshot.h index 58a5fd0902..7f01993f94 100644 --- a/src/gridcoin/accrual/snapshot.h +++ b/src/gridcoin/accrual/snapshot.h @@ -445,7 +445,7 @@ class AccrualSnapshot //! //! \brief Maps CPIDs to rewards accrued at the time of the snapshot. //! - //! Accrual values stored in units of of 1/100000000 GRC. + //! Accrual values stored in units of 1/100000000 GRC. //! AccrualMap m_records; diff --git a/src/gridcoin/beacon.cpp b/src/gridcoin/beacon.cpp index b719cc80c1..bbdf85bd24 100644 --- a/src/gridcoin/beacon.cpp +++ b/src/gridcoin/beacon.cpp @@ -1513,7 +1513,7 @@ bool BeaconRegistry::BeaconDB::erase(const uint256& hash) // // Note that this function acts very similarly to the map erase function with an iterator argument, but with a standard // pair returned. The first part of the pair a boolean as to whether the element was passivated, and the -// second is is an iterator to the next element. This is designed to be traversed in a for loop just like map erase. +// second is an iterator to the next element. This is designed to be traversed in a for loop just like map erase. std::pair BeaconRegistry::BeaconDB::passivate(BeaconRegistry::HistoricalBeaconMap::iterator& iter) { diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index 200990181f..048a3cd166 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -387,11 +387,11 @@ void TryProjectCpid(MiningId& mining_id, const MiningProject& project) //! A bug in BOINC sometimes results in an empty external CPID element in the //! client_state.xml file. For these cases, we'll recompute the external CPID //! of the project from the user's internal CPID and email address. This call -//! validates that the the user's email address hash extracted from a project +//! validates that the user's email address hash extracted from a project //! XML node matches the email set in the Gridcoin configuration file so that //! the wallet doesn't inadvertently generate an unowned CPID. //! -//! \param email_hash MD5 digest of the the email address to compare with +//! \param email_hash MD5 digest of the email address to compare with //! the configured email. //! \param internal_cpid As extracted from client_state.xml. An input to the //! hash that generates the external CPID. diff --git a/src/gridcoin/scraper/fwd.h b/src/gridcoin/scraper/fwd.h index 7550affcfa..d92d60d88f 100644 --- a/src/gridcoin/scraper/fwd.h +++ b/src/gridcoin/scraper/fwd.h @@ -166,7 +166,7 @@ struct ConvergedManifest std::multimap mIncludedScrapersbyProject; // ----- ScraperID ------- Project std::multimap mIncludedProjectsbyScraper; - // When bByParts (project) level convergence occurs, this records the the count of scrapers in the + // When bByParts (project) level convergence occurs, this records the count of scrapers in the // convergences by project. std::map mScraperConvergenceCountbyProject; diff --git a/src/gridcoin/scraper/scraper.cpp b/src/gridcoin/scraper/scraper.cpp index e49a671e40..0375b3b39e 100755 --- a/src/gridcoin/scraper/scraper.cpp +++ b/src/gridcoin/scraper/scraper.cpp @@ -4580,7 +4580,7 @@ bool ScraperConstructConvergedManifestByProject(const WhitelistSnapshot& project // If we meet the rule of CONVERGENCE_BY_PROJECT_RATIO, then proceed to fill out the rest of the map. if ((double)iCountSuccessfulConvergedProjects / (double)projectWhitelist.size() >= CONVERGENCE_BY_PROJECT_RATIO) { - // Fill out the the rest of the ConvergedManifest structure. Note this assumes one-to-one part to project statistics BLOB. Needs to + // Fill out the rest of the ConvergedManifest structure. Note this assumes one-to-one part to project statistics BLOB. Needs to // be fixed for more than one part per BLOB. This is easy in this case, because it is all from/referring to one manifest. // Lets use the BeaconList from the manifest referred to by nManifestHashForConvergedBeaconList. Technically there is no exact answer to @@ -4679,7 +4679,7 @@ bool ScraperConstructConvergedManifestByProject(const WhitelistSnapshot& project + " projects at " + DateTimeStrFormat("%x %H:%M:%S", StructConvergedManifest.timestamp)); - // Fill out the the excluded projects vector and the included scraper count (by project) map + // Fill out the excluded projects vector and the included scraper count (by project) map for (const auto& iProjects : projectWhitelist) { if (StructConvergedManifest.ConvergedManifestPartPtrsMap.find(iProjects.m_name) == StructConvergedManifest.ConvergedManifestPartPtrsMap.end()) diff --git a/src/gridcoin/scraper/scraper_net.h b/src/gridcoin/scraper/scraper_net.h index 572e5aff23..7bf3ac533c 100755 --- a/src/gridcoin/scraper/scraper_net.h +++ b/src/gridcoin/scraper/scraper_net.h @@ -73,7 +73,7 @@ class CSplitBlob }; -/** A objects holding info about the scraper data file we have or are downloading. */ +/** An objects holding info about the scraper data file we have or are downloading. */ class CScraperManifest : public CSplitBlob { diff --git a/src/gridcoin/voting/registry.cpp b/src/gridcoin/voting/registry.cpp index 345fd9b189..14700899a5 100644 --- a/src/gridcoin/voting/registry.cpp +++ b/src/gridcoin/voting/registry.cpp @@ -100,7 +100,7 @@ class PollClaimValidator //! \param claim The claim to verify minimum balance for. //! \param message Serialized context for claim signature verification. //! - //! \return \c true If the resolved amount for the the claim meets the + //! \return \c true If the resolved amount for the claim meets the //! minimum balance requirement to create a poll. //! bool VerifyClaim(const AddressClaim& claim, const ClaimMessage& message) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d9787a24fe..5e05826052 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -267,7 +267,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList= std::numeric_limits::min() && From c8785cad7a000d473426ff04987dfa3c9f0c4919 Mon Sep 17 00:00:00 2001 From: Pythonix <9782029+Pythonix@users.noreply.github.com> Date: Sat, 12 Jun 2021 06:59:38 +0000 Subject: [PATCH 185/280] remove stackblitz --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 48b340e4db..a1b7ccd993 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -22,7 +22,7 @@ labels: enhancement **Related Code** - + **Additional Context** From 03d5e3a3956fbd85ebd9212b0cc7a8a5fd5d09d3 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sat, 12 Jun 2021 12:41:47 -0400 Subject: [PATCH 186/280] Fix corner case problems with data directory chooser --- src/qt/bitcoin.cpp | 22 ++++++++++++---------- src/qt/intro.cpp | 28 ++++++++++++++++++---------- src/util/system.cpp | 5 ++--- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bb58c9c038..a5e625747a 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -274,6 +274,7 @@ int main(int argc, char *argv[]) tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); return EXIT_FAILURE; } + /** Check mainnet config file first in case testnet is set there and not in command line args **/ SelectParams(CBaseChainParams::MAIN); @@ -304,16 +305,6 @@ int main(int argc, char *argv[]) RegisterMetaTypes(); QApplication app(argc, argv); - // Not currently useful. - std::string error_msg; - - if (!gArgs.ReadConfigFiles(error_msg, true)) { - ThreadSafeMessageBox(strprintf("Error reading configuration file.\n"), - "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); - QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Cannot parse configuration file.")); - return EXIT_FAILURE; - } - #if defined(WIN32) && defined(QT_GUI) SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #endif @@ -388,6 +379,17 @@ int main(int argc, char *argv[]) // Gracefully exit if the user cancels if (!Intro::showIfNeeded(did_show_intro)) return EXIT_SUCCESS; + // Not currently useful. + std::string error_msg; + + if (!gArgs.ReadConfigFiles(error_msg, true)) { + ThreadSafeMessageBox(strprintf("Error reading configuration file.\n"), + "", CClientUIInterface::ICON_ERROR | CClientUIInterface::OK | CClientUIInterface::MODAL); + QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Cannot read configuration file. Please check the " + "path and format of the file.")); + return EXIT_FAILURE; + } + // Do this to pickup -testnet from the command line. SelectParams(gArgs.IsArgSet("-testnet") ? CBaseChainParams::TESTNET : CBaseChainParams::MAIN); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 1c21b882da..5866486ba3 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -166,19 +166,25 @@ void Intro::setDataDirectory(const QString &dataDir) bool Intro::showIfNeeded(bool& did_show_intro) { - did_show_intro = false; - QSettings settings; - /* If data directory provided on command line, no need to look at settings - or show a picking dialog */ - if(!gArgs.GetArg("-datadir", "").empty()) + // If data directory provided on command line AND -choosedatadir is not specified, no need to look at settings or + // show a picking dialog. The -choosedatadir test is required because showIfNeeded is called after the initialization + // of the optionsModel, which will populate the datadir from the GUI settings if present. Without it, you would then + // not be able to get the chooser dialog to come up again once a datadir is stored in the GUI settings config file. + if (!gArgs.GetArg("-datadir", "").empty() && !gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR)) { return true; + } /* 1) Default data directory for operating system */ QString dataDir = GUIUtil::getDefaultDataDirectory(); /* 2) Allow QSettings to override default dir */ dataDir = settings.value("dataDir", dataDir).toString(); - if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) + // This is necessary to handle the case where a user has changed the name of the data directory from a non-default + // back to the default, and wants to use the chooser via -choosedatadir to rechoose the data directory and have it + // properly respected without having to restart after the choosing. + bool originally_not_default_datadir = (dataDir != GUIUtil::getDefaultDataDirectory()); + + if (!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false)) @@ -197,9 +203,9 @@ bool Intro::showIfNeeded(bool& did_show_intro) intro.setWindowIcon(QIcon(":/images/gridcoin")); did_show_intro = true; - while(true) + while (true) { - if(!intro.exec()) + if (!intro.exec()) { /* Cancel clicked */ return false; @@ -222,8 +228,10 @@ bool Intro::showIfNeeded(bool& did_show_intro) * override -datadir in the bitcoin.conf file in the default data directory * (to be consistent with bitcoind behavior) */ - if (dataDir != GUIUtil::getDefaultDataDirectory()) { - gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting + if (dataDir != GUIUtil::getDefaultDataDirectory() || originally_not_default_datadir) { + // This must be a ForceSetArg, because the optionsModel has already loaded the datadir argument if it exists in + // the Qt settings file prior to this. + gArgs.ForceSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting } return true; diff --git a/src/util/system.cpp b/src/util/system.cpp index a74c77970d..b1d35eb171 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -339,12 +339,11 @@ const fs::path& ArgsManager::GetDataDirPath(bool net_specific) const if (net_specific) path /= BaseParams().DataDir(); - /* Reserved for future Bitcoin backport functionality with wallets. if (fs::create_directories(path)) { // This is the first run, create wallets subdirectory too - fs::create_directories(path / "wallets"); + // Reserved for when we move wallets to a subdir like Bitcoin + //fs::create_directories(path / "wallets"); } - */ path = StripRedundantLastElementsOfPath(path); return path; From fc6101db49e3d9b8e14c245d3161678940c48fe0 Mon Sep 17 00:00:00 2001 From: barton26 Date: Sat, 12 Jun 2021 14:59:16 -0400 Subject: [PATCH 187/280] Add Discord --- doc/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index 9e14d3b510..6e92265816 100644 --- a/doc/README.md +++ b/doc/README.md @@ -15,7 +15,8 @@ To download Gridcoin, visit [gridcoin.us](https://gridcoin.us). * See the documentation at the [Gridcoin Wiki](https://wiki.gridcoin.us/Main_Page) for help and more information. * A lot of features core features are based on Bitcoin and have been documented on the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) -* Ask for help or discuss on [#gridcoin](https://webchat.freenode.net?channels=gridcoin) on Freenode. +* For general questions, please visit our Discord server at https://discord.gg/jf9XX4a +* Ask for help or discuss on [#gridcoin](https://webchat.freenode.net?channels=gridcoin) on Freenode * You can also join us on [Slack](https://grcinvite.herokuapp.com/) with the invite Token GRCsquad Building From 2139aae02a8b5549c7d927cd24d602face353f15 Mon Sep 17 00:00:00 2001 From: barton26 Date: Sat, 12 Jun 2021 16:06:06 -0400 Subject: [PATCH 188/280] Update doc/README.md Co-authored-by: RoboticMind <30132912+RoboticMind@users.noreply.github.com> --- doc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index 6e92265816..92b2c80436 100644 --- a/doc/README.md +++ b/doc/README.md @@ -17,7 +17,7 @@ for help and more information. * A lot of features core features are based on Bitcoin and have been documented on the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Main_Page) * For general questions, please visit our Discord server at https://discord.gg/jf9XX4a * Ask for help or discuss on [#gridcoin](https://webchat.freenode.net?channels=gridcoin) on Freenode -* You can also join us on [Slack](https://grcinvite.herokuapp.com/) with the invite Token GRCsquad +* You can also join us on [Slack](https://join.slack.com/t/teamgridcoin/shared_invite/enQtMjk2NTI4MzAwMzg0LTE4N2I3ZWZjYWJlZGM1Zjg3MTUyMDhiN2M5NmRmZTA2NDA0ZmY1ZTFmOGM3ZGU2YTBkOTdhNTk2ZjkzMGZkODY/)``` Building --------------------- From 3dec942c6c883c3f391943f17094d631800ab4a5 Mon Sep 17 00:00:00 2001 From: barton26 Date: Sat, 12 Jun 2021 18:39:22 -0400 Subject: [PATCH 189/280] net: Drop support of the insecure miniUPnPc versions Minimum supported miniUPnPc API version is set to 10. This prevents numerous security issues by enforcing a 2015 UPnP version (keeps compatibility with Ubuntu 16.04 LTS and Debian 8) Ref: https://github.com/bitcoin/bitcoin/pull/15993 --- configure.ac | 22 +++++++++++++++++++++- src/net.cpp | 19 +++++-------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index dbdbff1c2b..303cf1809d 100755 --- a/configure.ac +++ b/configure.ac @@ -807,6 +807,26 @@ if test x$use_upnp != xno; then [AC_CHECK_LIB([miniupnpc], [main],[MINIUPNPC_LIBS=-lminiupnpc], [have_miniupnpc=no])], [have_miniupnpc=no] ) +dnl The minimum supported miniUPnPc API version is set to 10. This keeps compatibility +dnl with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. +if test x$have_miniupnpc != xno; then + AC_MSG_CHECKING([whether miniUPnPc API version is supported]) + AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if MINIUPNPC_API_VERSION >= 10 + // Everything is okay + #else + # error miniUPnPc API version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + AC_MSG_WARN([miniUPnPc API version < 10 is unsupported, disabling UPnP support.]) + have_miniupnpc=no + ]) +fi fi @@ -1061,7 +1081,7 @@ dnl enable upnp support AC_MSG_CHECKING([whether to build with support for UPnP]) if test x$have_miniupnpc = xno; then if test x$use_upnp = xyes; then - AC_MSG_ERROR("UPnP requested but cannot be built. use --without-miniupnpc") + AC_MSG_ERROR("UPnP requested but cannot be built. Use --without-miniupnpc.") fi AC_MSG_RESULT(no) else diff --git a/src/net.cpp b/src/net.cpp index f11997a3d8..88bedb2c72 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -31,6 +31,9 @@ #include #include #include +// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility +// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. +static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); #endif using namespace std; @@ -1147,16 +1150,10 @@ void ThreadMapPort2(void* parg) struct UPNPDev * devlist = 0; char lanaddr[64]; -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); -#elif MINIUPNPC_API_VERSION < 14 - /* miniupnpc 1.6 */ int error = 0; +#if MINIUPNPC_API_VERSION < 14 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); #else - /* miniupnpc 1.9.20150730 */ - int error = 0; devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); #endif @@ -1185,15 +1182,9 @@ void ThreadMapPort2(void* parg) } string strDesc = "Gridcoin " + FormatFullVersion(); -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); -#else - /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); -#endif if(r!=UPNPCOMMAND_SUCCESS) LogPrintf("AddPortMapping(%s, %s, %s) unsuccessful with code %d (%s)", From 71dac558fdba9c3993cb19076232bc649a335a5e Mon Sep 17 00:00:00 2001 From: barton26 Date: Sat, 12 Jun 2021 19:04:35 -0400 Subject: [PATCH 190/280] build: miniupnpc 2.2.2 Security Ref: https://github.com/bitcoin/bitcoin/pull/20421 --- depends/packages/miniupnpc.mk | 12 +++---- .../patches/miniupnpc/dont_leak_info.patch | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 depends/patches/miniupnpc/dont_leak_info.patch diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk index fc46d00f6f..bb07971b71 100644 --- a/depends/packages/miniupnpc.mk +++ b/depends/packages/miniupnpc.mk @@ -1,9 +1,9 @@ package=miniupnpc -GCCFLAGS?= -$(package)_version=2.0.20180203 -$(package)_download_path=http://miniupnp.free.fr/files +$(package)_version=2.2.2 +$(package)_download_path=https://miniupnp.tuxfamily.org/files/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=90dda8c7563ca6cd4a83e23b3c66dbbea89603a1675bfdb852897c2c9cc220b7 +$(package)_sha256_hash=888fb0976ba61518276fe1eda988589c700a3f2a69d71089260d75562afd3687 +$(package)_patches=dont_leak_info.patch define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" @@ -18,9 +18,7 @@ define $(package)_set_vars endef define $(package)_preprocess_cmds - mkdir dll && \ - sed -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"$($(package)_version)\"|' -e 's|OS/version|$(host)|' miniupnpcstrings.h.in > miniupnpcstrings.h && \ - sed -i.old "s|miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings|miniupnpcstrings.h: miniupnpcstrings.h.in|" Makefile.mingw + patch -p1 < $($(package)_patch_dir)/dont_leak_info.patch endef define $(package)_build_cmds diff --git a/depends/patches/miniupnpc/dont_leak_info.patch b/depends/patches/miniupnpc/dont_leak_info.patch new file mode 100644 index 0000000000..512f9c50ea --- /dev/null +++ b/depends/patches/miniupnpc/dont_leak_info.patch @@ -0,0 +1,32 @@ +commit 8815452257437ba36607d0e2381c01142d1c7bb0 +Author: fanquake +Date: Thu Nov 19 10:51:19 2020 +0800 + + Don't leak OS and miniupnpc version info in User-Agent + +diff --git a//minisoap.c b/minisoap.c +index 7860667..775580b 100644 +--- a/minisoap.c ++++ b/minisoap.c +@@ -90,7 +90,7 @@ int soapPostSubmit(SOCKET fd, + headerssize = snprintf(headerbuf, sizeof(headerbuf), + "POST %s HTTP/%s\r\n" + "Host: %s%s\r\n" +- "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" ++ "User-Agent: " UPNP_VERSION_STRING "\r\n" + "Content-Length: %d\r\n" + "Content-Type: text/xml\r\n" + "SOAPAction: \"%s\"\r\n" +diff --git a/miniwget.c b/miniwget.c +index d5b7970..05aeb9c 100644 +--- a/miniwget.c ++++ b/miniwget.c +@@ -444,7 +444,7 @@ miniwget3(const char * host, + "GET %s HTTP/%s\r\n" + "Host: %s:%d\r\n" + "Connection: Close\r\n" +- "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" ++ "User-Agent: " UPNP_VERSION_STRING "\r\n" + + "\r\n", + path, httpversion, host, port); From 0670e573dbc5bbbad9f057433da635da3e0ef76d Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 13 Jun 2021 13:17:53 +0300 Subject: [PATCH 191/280] scripted-diff: arrow deref -BEGIN VERIFY SCRIPT- sed -i -r -e 's/([^a-zA-Z0-9])\(\*(\w+)\)\./\1\2->/g' $(git ls-files -- 'src/*.h' 'src/*.cpp' ':!src/leveldb/**' ':!src/univalue/**' ':!src/qt/locale') git diff -U0 | contrib/devtools/clang-format-diff.py -p1 -i -v -END VERIFY SCRIPT- --- src/addrman.cpp | 12 ++--- src/addrman.h | 6 +-- src/alert.cpp | 6 +-- src/banman.cpp | 6 +-- src/init.cpp | 4 +- src/keystore.cpp | 12 ++--- src/keystore.h | 6 +-- src/main.cpp | 26 +++++----- src/main.h | 8 ++-- src/miner.cpp | 4 +- src/net.h | 2 +- src/prevector.h | 2 +- src/qt/transactionrecord.cpp | 2 +- src/rpc/blockchain.h | 4 +- src/rpc/rawtransaction.cpp | 7 ++- src/rpc/server.cpp | 2 +- src/sync.cpp | 2 +- src/test/gridcoin/cpid_tests.cpp | 22 +++++++-- src/txdb-leveldb.cpp | 4 +- src/validation.cpp | 2 +- src/wallet/db.cpp | 4 +- src/wallet/rpcwallet.cpp | 50 ++++++++++--------- src/wallet/wallet.cpp | 82 ++++++++++++++++---------------- src/wallet/wallet.h | 2 +- src/wallet/walletdb.cpp | 8 ++-- 25 files changed, 147 insertions(+), 138 deletions(-) mode change 100755 => 100644 src/rpc/rawtransaction.cpp diff --git a/src/addrman.cpp b/src/addrman.cpp index 2f50117fec..892d567782 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -84,10 +84,10 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int *pnId) if (it == mapAddr.end()) return NULL; if (pnId) - *pnId = (*it).second; - std::map::iterator it2 = mapInfo.find((*it).second); + *pnId = it->second; + std::map::iterator it2 = mapInfo.find(it->second); if (it2 != mapInfo.end()) - return &(*it2).second; + return &it2->second; return NULL; } @@ -208,7 +208,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin) // remove the entry from all new buckets for (std::vector >::iterator it = vvNew.begin(); it != vvNew.end(); it++) { - if ((*it).erase(nId)) + if (it->erase(nId)) info.nRefCount--; } nNew--; @@ -438,8 +438,8 @@ int CAddrMan::Check_() for (std::map::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { - int n = (*it).first; - CAddrInfo &info = (*it).second; + int n = it->first; + CAddrInfo& info = it->second; if (info.fInTried) { diff --git a/src/addrman.h b/src/addrman.h index a481cb37c4..4e2cf543df 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -287,8 +287,8 @@ class CAddrMan int nIds = 0; for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { if (nIds == nNew) break; // this means nNew was wrong, oh ow - mapUnkIds[(*it).first] = nIds; - const CAddrInfo &info = (*it).second; + mapUnkIds[it->first] = nIds; + const CAddrInfo& info = it->second; if (info.nRefCount) { s << info; nIds++; @@ -297,7 +297,7 @@ class CAddrMan nIds = 0; for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { if (nIds == nTried) break; // this means nTried was wrong, oh ow - const CAddrInfo &info = (*it).second; + const CAddrInfo& info = it->second; if (info.fInTried) { s << info; nIds++; diff --git a/src/alert.cpp b/src/alert.cpp index 6af50048fb..5385675fa6 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -231,17 +231,17 @@ bool CAlert::ProcessAlert(bool fThread) // Cancel previous alerts for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) { - const CAlert& alert = (*mi).second; + const CAlert& alert = mi->second; if (Cancels(alert)) { LogPrint(BCLog::LogFlags::VERBOSE, "cancelling alert %d", alert.nID); - uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); + uiInterface.NotifyAlertChanged(mi->first, CT_DELETED); mapAlerts.erase(mi++); } else if (!alert.IsInEffect()) { LogPrint(BCLog::LogFlags::VERBOSE, "expiring alert %d", alert.nID); - uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); + uiInterface.NotifyAlertChanged(mi->first, CT_DELETED); mapAlerts.erase(mi++); } else diff --git a/src/banman.cpp b/src/banman.cpp index e3ba279893..8dd49a663c 100644 --- a/src/banman.cpp +++ b/src/banman.cpp @@ -116,7 +116,7 @@ bool BanMan::IsBanned(CSubNet sub_net) LOCK(m_cs_banned); banmap_t::iterator i = m_banned.find(sub_net); if (i != m_banned.end()) { - CBanEntry ban_entry = (*i).second; + CBanEntry ban_entry = i->second; if (current_time < ban_entry.nBanUntil) { return true; } @@ -199,8 +199,8 @@ void BanMan::SweepBanned() LOCK(m_cs_banned); banmap_t::iterator it = m_banned.begin(); while (it != m_banned.end()) { - CSubNet sub_net = (*it).first; - CBanEntry ban_entry = (*it).second; + CSubNet sub_net = it->first; + CBanEntry ban_entry = it->second; if (now > ban_entry.nBanUntil) { m_banned.erase(it++); diff --git a/src/init.cpp b/src/init.cpp index 03d6e10ab2..331237973e 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -1175,10 +1175,10 @@ bool AppInit2(ThreadHandlerPtr threads) int nFound = 0; for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { - uint256 hash = (*mi).first; + uint256 hash = mi->first; if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; CBlock block; block.ReadFromDisk(pindex); block.print(); diff --git a/src/keystore.cpp b/src/keystore.cpp index 025508a433..e9a770b9d4 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -56,7 +56,7 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) ScriptMap::const_iterator mi = mapScripts.find(hash); if (mi != mapScripts.end()) { - redeemScriptOut = (*mi).second; + redeemScriptOut = mi->second; return true; } } @@ -100,8 +100,8 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); for (; mi != mapCryptedKeys.end(); ++mi) { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector &vchCryptedSecret = (*mi).second.second; + const CPubKey& vchPubKey = mi->second.first; + const std::vector& vchCryptedSecret = mi->second.second; CSecret vchSecret; if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) return false; @@ -165,8 +165,8 @@ bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector &vchCryptedSecret = (*mi).second.second; + const CPubKey& vchPubKey = mi->second.first; + const std::vector& vchCryptedSecret = mi->second.second; CSecret vchSecret; if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) return false; @@ -190,7 +190,7 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { - vchPubKeyOut = (*mi).second.first; + vchPubKeyOut = mi->second.first; return true; } } diff --git a/src/keystore.h b/src/keystore.h index ab369bbf47..349c64b91d 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -73,7 +73,7 @@ class CBasicKeyStore : public CKeyStore KeyMap::const_iterator mi = mapKeys.begin(); while (mi != mapKeys.end()) { - setAddress.insert((*mi).first); + setAddress.insert(mi->first); mi++; } } @@ -86,7 +86,7 @@ class CBasicKeyStore : public CKeyStore if (mi != mapKeys.end()) { keyOut.Reset(); - keyOut.SetSecret((*mi).second.first, (*mi).second.second); + keyOut.SetSecret(mi->second.first, mi->second.second); return true; } } @@ -170,7 +170,7 @@ class CCryptoKeyStore : public CBasicKeyStore CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); while (mi != mapCryptedKeys.end()) { - setAddress.insert((*mi).first); + setAddress.insert(mi->first); mi++; } } diff --git a/src/main.cpp b/src/main.cpp index 1f9662b443..d6b95824c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -475,7 +475,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; if (!pindex || !pindex->IsInMainChain()) return 0; @@ -718,7 +718,7 @@ void CTxMemPool::queryHashes(std::vector& vtxid) LOCK(cs); vtxid.reserve(mapTx.size()); for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back((*mi).first); + vtxid.push_back(mi->first); } int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const @@ -731,7 +731,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; if (!pindex || !pindex->IsInMainChain()) return 0; @@ -1643,7 +1643,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // Write queued txindex changes for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { - if (!txdb.UpdateTxIndex((*mi).first, (*mi).second)) + if (!txdb.UpdateTxIndex(mi->first, mi->second)) return error("ConnectBlock[] : UpdateTxIndex failed"); } @@ -2065,7 +2065,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u BlockMap::iterator miPrev = mapBlockIndex.find(hashPrevBlock); if (miPrev != mapBlockIndex.end()) { - pindexNew->pprev = (*miPrev).second; + pindexNew->pprev = miPrev->second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } @@ -2087,7 +2087,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u // Add to mapBlockIndex BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); + pindexNew->phashBlock = &(mi->first); // Write to disk block index CTxDB txdb; @@ -2278,7 +2278,7 @@ bool CBlock::AcceptBlock(bool generated_by_me) BlockMap::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) return DoS(10, error("AcceptBlock() : prev block not found")); - CBlockIndex* pindexPrev = (*mi).second; + CBlockIndex* pindexPrev = mi->second; const int nHeight = pindexPrev->nHeight + 1; const int checkpoint_height = Params().Checkpoints().GetHeight(); @@ -2884,7 +2884,7 @@ void PrintBlockTree() map > mapNext; for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; mapNext[pindex->pprev].push_back(pindex); } @@ -3328,7 +3328,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) - ((*mi).second)->PushAddress(addr); + (mi->second)->PushAddress(addr); } } // Do not store addresses outside our network @@ -3433,7 +3433,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (mi != mapBlockIndex.end()) { CBlock block; - block.ReadFromDisk((*mi).second); + block.ReadFromDisk(mi->second); pfrom->PushMessage("encrypt", block); @@ -3458,7 +3458,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_mapRelay); map::iterator mi = mapRelay.find(inv); if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); + pfrom->PushMessage(inv.GetCommand(), mi->second); pushed = true; } } @@ -3567,7 +3567,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BlockMap::iterator mi = mapBlockIndex.find(hashStop); if (mi == mapBlockIndex.end()) return true; - pindex = (*mi).second; + pindex = mi->second; } else { @@ -3725,7 +3725,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, map::iterator mi = pfrom->mapRequests.find(hashReply); if (mi != pfrom->mapRequests.end()) { - tracker = (*mi).second; + tracker = mi->second; pfrom->mapRequests.erase(mi); } } diff --git a/src/main.h b/src/main.h index 7119f78a18..7f2351561f 100644 --- a/src/main.h +++ b/src/main.h @@ -1033,7 +1033,7 @@ class CBlockLocator { BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end()) - Set((*mi).second); + Set(mi->second); } CBlockLocator(const std::vector& vHaveIn) @@ -1091,7 +1091,7 @@ class CBlockLocator BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; if (pindex->IsInMainChain()) return nDistance; } @@ -1110,7 +1110,7 @@ class CBlockLocator BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; if (pindex->IsInMainChain()) return pindex; } @@ -1126,7 +1126,7 @@ class CBlockLocator BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; if (pindex->IsInMainChain()) return hash; } diff --git a/src/miner.cpp b/src/miner.cpp index 55e5b02061..368b71f47c 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -306,7 +306,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { - CTransaction& tx = (*mi).second; + CTransaction& tx = mi->second; if (tx.IsCoinBase() || tx.IsCoinStake() || !IsFinalTx(tx, nHeight)) continue; @@ -395,7 +395,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) } else { - vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); + vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &mi->second)); } } diff --git a/src/net.h b/src/net.h index 11f8da096b..1849d9985a 100644 --- a/src/net.h +++ b/src/net.h @@ -495,7 +495,7 @@ class CNode std::deque::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); ssSend.GetAndClear(*it); - nSendSize += (*it).size(); + nSendSize += it->size(); // If write queue empty, attempt "optimistic write" if (it == vSendMsg.begin()) diff --git a/src/prevector.h b/src/prevector.h index 9d576321b6..df39d61384 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -408,7 +408,7 @@ class prevector { char* endp = (char*)&(*end()); if (!std::is_trivially_destructible::value) { while (p != last) { - (*p).~T(); + p->~T(); _size--; ++p; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index ee98f8f5ea..0940bee350 100755 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -356,7 +356,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) CBlockIndex* pindex = NULL; BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock); if (mi != mapBlockIndex.end()) - pindex = (*mi).second; + pindex = mi->second; // Sort order, unrecorded transactions sort to the top status.sortKey = strprintf("%010d-%01d-%010u-%03d", diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 7d748f0668..08d0ea6c2d 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -47,14 +47,14 @@ class MockBlockIndex : CDiskBlockIndex // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) - return (*mi).second; + return mi->second; // Create new CBlockIndex* pindexNew = GRC::BlockIndexPool::GetNextBlockIndex(); if (!pindexNew) throw std::runtime_error("LoadBlockIndex() : new CBlockIndex failed"); mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); + pindexNew->phashBlock = &(mi->first); return pindexNew; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp old mode 100755 new mode 100644 index f91e956062..e659e9adf8 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -46,7 +46,7 @@ std::vector> GetTxStakeBoincHashInfo(const C return res; } - pindex = (*mi).second; + pindex = mi->second; if (!block.ReadFromDisk(pindex)) { @@ -346,9 +346,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { entry.pushKV("blockhash", hashBlock.GetHex()); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; + if (mi != mapBlockIndex.end() && mi->second) { + CBlockIndex* pindex = mi->second; if (pindex->IsInMainChain()) { entry.pushKV("confirmations", 1 + nBestHeight - pindex->nHeight); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 0a0b0f36b9..2963d0fe79 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -467,7 +467,7 @@ const CRPCCommand *CRPCTable::operator[](string name) const map::const_iterator it = mapCommands.find(name); if (it == mapCommands.end()) return NULL; - return (*it).second; + return it->second; } void ErrorReply(std::ostream& stream, const UniValue& objError, const UniValue& id) diff --git a/src/sync.cpp b/src/sync.cpp index ea5f2b420a..57e000d353 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -122,7 +122,7 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) static void pop_lock() { - (*lockstack).pop_back(); + lockstack->pop_back(); } void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) diff --git a/src/test/gridcoin/cpid_tests.cpp b/src/test/gridcoin/cpid_tests.cpp index bfd6ab14a9..ceaac8fb02 100644 --- a/src/test/gridcoin/cpid_tests.cpp +++ b/src/test/gridcoin/cpid_tests.cpp @@ -318,10 +318,24 @@ BOOST_AUTO_TEST_CASE(it_parses_a_cpid_mining_id) if (const GRC::CpidOption cpid = mining_id.TryCpid()) { BOOST_CHECK(*cpid == expected); - BOOST_CHECK((*cpid).Raw() == (std::array { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, - })); + BOOST_CHECK(cpid->Raw() == (std::array{ + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + })); } else { BOOST_FAIL("MiningId variant does not contain the CPID."); } diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp index 2c5857b265..60922ef797 100644 --- a/src/txdb-leveldb.cpp +++ b/src/txdb-leveldb.cpp @@ -291,14 +291,14 @@ static CBlockIndex *InsertBlockIndex(const uint256& hash) // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) - return (*mi).second; + return mi->second; // Create new CBlockIndex* pindexNew = GRC::BlockIndexPool::GetNextBlockIndex(); if (!pindexNew) throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); + pindexNew->phashBlock = &(mi->first); return pindexNew; } diff --git a/src/validation.cpp b/src/validation.cpp index d819b442bb..03e25b8460 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -518,7 +518,7 @@ int GetDepthInMainChain(const CTxIndex& txi) BlockMap::iterator mi = mapBlockIndex.find(block.GetHash(true)); if (mi == mapBlockIndex.end()) return 0; - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = mi->second; if (!pindex || !pindex->IsInMainChain()) return 0; return 1 + nBestHeight - pindex->nHeight; diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 25e38852b7..2eb6a387dc 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -460,8 +460,8 @@ void CDBEnv::Flush(bool fShutdown) map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { - string strFile = (*mi).first; - int nRefCount = (*mi).second; + string strFile = mi->first; + int nRefCount = mi->second; LogPrint(BCLog::LogFlags::WALLETDB, "%s refcount=%d", strFile, nRefCount); if (nRefCount == 0) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index fe1f3b8359..651abffd88 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -250,7 +250,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; for (auto const& txout : wtx.vout) if (txout.scriptPubKey == scriptPubKey) bKeyUsed = true; @@ -339,8 +339,8 @@ UniValue getaccount(const UniValue& params, bool fHelp) string strAccount; map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty()) - strAccount = (*mi).second; + if (mi != pwalletMain->mapAddressBook.end() && !mi->second.empty()) + strAccount = mi->second; return strAccount; } @@ -553,7 +553,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) int64_t nAmount = 0; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) continue; @@ -604,7 +604,7 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) int64_t nAmount = 0; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) continue; @@ -627,7 +627,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi // Tally wallet transactions for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; if (!IsFinalTx(wtx) || wtx.GetDepthInMainChain() < 0) continue; @@ -1049,7 +1049,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; if (!wtx.IsTrusted()) continue; @@ -1229,7 +1229,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) map mapTally; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) continue; @@ -1276,9 +1276,9 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) bool fIsWatchonly = false; if (it != mapTally.end()) { - nAmount = (*it).second.nAmount; - nConf = (*it).second.nConf; - fIsWatchonly = (*it).second.fIsWatchonly; + nAmount = it->second.nAmount; + nConf = it->second.nConf; + fIsWatchonly = it->second.fIsWatchonly; } if (fByAccounts) @@ -1301,11 +1301,10 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) UniValue transactions(UniValue::VARR); if(it != mapTally.end()) { - for (const uint256& _item : (*it).second.txids) - { + for (const uint256& _item : it->second.txids) { transactions.push_back(_item.GetHex()); } - } + } obj.pushKV("txids", transactions); ret.push_back(obj); } @@ -1315,12 +1314,12 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) { for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) { - int64_t nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; + int64_t nAmount = it->second.nAmount; + int nConf = it->second.nConf; UniValue obj(UniValue::VOBJ); - if((*it).second.fIsWatchonly) - obj.pushKV("involvesWatchonly", true); - obj.pushKV("account", (*it).first); + if (it->second.fIsWatchonly) + obj.pushKV("involvesWatchonly", true); + obj.pushKV("account", it->first); obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); ret.push_back(obj); @@ -1626,10 +1625,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp) // iterate backwards until we have nCount items to return: for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - CWalletTx *const pwtx = (*it).second.first; + CWalletTx* const pwtx = it->second.first; if (pwtx != 0) ListTransactions(*pwtx, strAccount, 0, true, ret, filter); - CAccountingEntry *const pacentry = (*it).second.second; + CAccountingEntry* const pacentry = it->second.second; if (pacentry != 0) AcentryToJSON(*pacentry, strAccount, ret); @@ -1749,7 +1748,7 @@ UniValue listaccounts(const UniValue& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = it->second; int64_t nFee; string strSentAccount; list listReceived; @@ -1832,7 +1831,7 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) { - CWalletTx tx = (*it).second; + CWalletTx tx = it->second; if (depth == -1 || tx.GetDepthInMainChain() < depth) ListTransactions(tx, "*", 0, true, transactions, filter); @@ -1915,9 +1914,8 @@ UniValue gettransaction(const UniValue& params, bool fHelp) { entry.pushKV("blockhash", hashBlock.GetHex()); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; + if (mi != mapBlockIndex.end() && mi->second) { + CBlockIndex* pindex = mi->second; if (pindex->IsInMainChain()) entry.pushKV("confirmations", 1 + nBestHeight - pindex->nHeight); else diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 35c31bbbf2..54d3b19493 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -406,7 +406,7 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, // would make this much faster for applications that do this a lot. for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - CWalletTx* wtx = &((*it).second); + CWalletTx* wtx = &(it->second); txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); } acentries.clear(); @@ -431,7 +431,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock, CWalletDB* map::iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { - CWalletTx& wtx = (*mi).second; + CWalletTx& wtx = mi->second; if (txin.prevout.n >= wtx.vout.size()) LogPrintf("WalletUpdateSpent: bad wtx %s", wtx.GetHash().ToString()); else if (!wtx.IsSpent(txin.prevout.n) && (IsMine(wtx.vout[txin.prevout.n]) != ISMINE_NO)) @@ -448,7 +448,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock, CWalletDB* { uint256 hash = tx.GetHash(); map::iterator mi = mapWallet.find(hash); - CWalletTx& wtx = (*mi).second; + CWalletTx& wtx = mi->second; for (auto const& txout : tx.vout) { @@ -617,7 +617,7 @@ isminetype CWallet::IsMine(const CTxIn &txin) const map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { - const CWalletTx& prev = (*mi).second; + const CWalletTx& prev = mi->second; if (txin.prevout.n < prev.vout.size()) return IsMine(prev.vout[txin.prevout.n]); } @@ -632,7 +632,7 @@ int64_t CWallet::GetDebit(const CTxIn &txin,const isminefilter& filter) const map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { - const CWalletTx& prev = (*mi).second; + const CWalletTx& prev = mi->second; if (txin.prevout.n < prev.vout.size()) if (IsMine(prev.vout[txin.prevout.n]) & filter) return prev.vout[txin.prevout.n].nValue; @@ -680,7 +680,7 @@ int CWalletTx::GetRequestCount() const { map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; + nRequests = mi->second; } } else @@ -689,14 +689,14 @@ int CWalletTx::GetRequestCount() const map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); if (mi != pwallet->mapRequestCount.end()) { - nRequests = (*mi).second; + nRequests = mi->second; // How about the block it's in? if (nRequests == 0 && !hashBlock.IsNull()) { map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; + nRequests = mi->second; else nRequests = 1; // If it's in someone else's block it must have got out } @@ -919,7 +919,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, if (pwallet->mapAddressBook.count(r.destination)) { map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); - if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) + if (mi != pwallet->mapAddressBook.end() && mi->second == strAccount) nReceived += r.amount; } else if (strAccount.empty()) @@ -957,8 +957,8 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) map::const_iterator mi = pwallet->mapWallet.find(hash); if (mi != pwallet->mapWallet.end()) { - tx = (*mi).second; - for (auto const& txWalletPrev : (*mi).second.vtxPrev) + tx = mi->second; + for (auto const& txWalletPrev : mi->second.vtxPrev) mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; } else if (mapWalletPrev.count(hash)) @@ -1213,7 +1213,7 @@ int64_t CWallet::GetBalance() const LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &it->second; if (pcoin->IsTrusted() && (pcoin->IsConfirmed() || pcoin->fFromMe)) nTotal += pcoin->GetAvailableCredit(); } @@ -1229,7 +1229,7 @@ int64_t CWallet::GetUnconfirmedBalance() const LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &it->second; if (!IsFinalTx(*pcoin) || (!pcoin->IsConfirmed() && !pcoin->fFromMe && pcoin->IsInMainChain())) nTotal += pcoin->GetAvailableCredit(); } @@ -1244,7 +1244,7 @@ int64_t CWallet::GetImmatureBalance() const LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx& pcoin = (*it).second; + const CWalletTx& pcoin = it->second; if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain()) nTotal += GetCredit(pcoin); } @@ -1261,25 +1261,24 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx* pcoin = &(*it).second; - int nDepth = pcoin->GetDepthInMainChain(); + const CWalletTx* pcoin = &it->second; + int nDepth = pcoin->GetDepthInMainChain(); - if (!fIncludeStakedCoins) - { - if (!IsFinalTx(*pcoin)) - continue; + if (!fIncludeStakedCoins) { + if (!IsFinalTx(*pcoin)) + continue; - if (fOnlyConfirmed && !pcoin->IsTrusted()) - continue; + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; - if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) - continue; + if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) + continue; - if (nDepth < 0) - continue; + if (nDepth < 0) + continue; } else { @@ -1289,12 +1288,11 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const for (unsigned int i = 0; i < pcoin->vout.size(); i++) { if ((!(pcoin->IsSpent(i)) && (IsMine(pcoin->vout[i]) != ISMINE_NO) && pcoin->vout[i].nValue >= nMinimumInputValue && - (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) - || (fIncludeStakedCoins && pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)) - { - vCoins.push_back(COutput(pcoin, i, nDepth)); - } - } + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected(it->first, i))) || + (fIncludeStakedCoins && pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)) { + vCoins.push_back(COutput(pcoin, i, nDepth)); + } + } } } @@ -1452,7 +1450,7 @@ int64_t CWallet::GetStake() const LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &it->second; if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) nTotal += CWallet::GetCredit(*pcoin); } @@ -1465,7 +1463,7 @@ int64_t CWallet::GetNewMint() const LOCK2(cs_main, cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &it->second; if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) nTotal += CWallet::GetCredit(*pcoin); } @@ -2286,7 +2284,7 @@ bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) map::iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) { - wtx = (*mi).second; + wtx = mi->second; return true; } } @@ -2559,7 +2557,7 @@ set< set > CWallet::GetAddressGroupings() map< CTxDestination, set* >::iterator it; for (auto const& address : grouping) if ((it = setmap.find(address)) != setmap.end()) - hits.insert((*it).second); + hits.insert(it->second); // merge all hit groups into a new single group and delete old groups set* merged = new set(grouping); @@ -2597,7 +2595,7 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64_t& nBalanceInQuestion, bo vector vCoins; vCoins.reserve(mapWallet.size()); for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); + vCoins.push_back(&it->second); CWalletDB walletdb(strWalletFile); @@ -2653,7 +2651,7 @@ void CWallet::DisableTransaction(const CTransaction &tx) map::iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { - CWalletTx& prev = (*mi).second; + CWalletTx& prev = mi->second; if (txin.prevout.n < prev.vout.size() && (IsMine(prev.vout[txin.prevout.n]) != ISMINE_NO)) { prev.MarkUnspent(txin.prevout.n); @@ -2832,7 +2830,7 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { std::vector vAffected; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { // iterate over all wallet transactions... - const CWalletTx &wtx = (*it).second; + const CWalletTx& wtx = it->second; BlockMap::iterator blit = mapBlockIndex.find(wtx.hashBlock); if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) { // ... which are already in a block @@ -2882,7 +2880,7 @@ MinedType GetGeneratedType(const CWallet *wallet, const uint256& tx, unsigned in if (mi == mapBlockIndex.end()) return MinedType::UNKNOWN; - CBlockIndex* blkindex = (*mi).second; + CBlockIndex* blkindex = mi->second; // If we are calling GetGeneratedType, this is a transaction // that corresponds (is integral to) the block. We check whether diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 520e0225e1..881ecba0cb 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -355,7 +355,7 @@ class CWallet : public CCryptoKeyStore LOCK(cs_wallet); std::map::iterator mi = mapRequestCount.find(hash); if (mi != mapRequestCount.end()) - (*mi).second++; + mi->second++; } } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index ec1ae0cd87..79191b4c97 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -137,7 +137,7 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { - CWalletTx* wtx = &((*it).second); + CWalletTx* wtx = &(it->second); txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); } list acentries; @@ -152,8 +152,8 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) std::vector nOrderPosOffsets; for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { - CWalletTx *const pwtx = (*it).second.first; - CAccountingEntry *const pacentry = (*it).second.second; + CWalletTx* const pwtx = it->second.first; + CAccountingEntry* const pacentry = it->second.second; int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) @@ -658,7 +658,7 @@ void ThreadFlushWalletDB(void* parg) map::iterator mi = bitdb.mapFileUseCount.begin(); while (mi != bitdb.mapFileUseCount.end()) { - nRefCount += (*mi).second; + nRefCount += mi->second; mi++; } From 43a49cb26767795516e1903925a12e2341490bb5 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 13 Jun 2021 13:20:49 +0300 Subject: [PATCH 192/280] scripted-diff: boost foreach -> c++11 for -BEGIN VERIFY SCRIPT- sed -i -r -e 's/BOOST_FOREACH\([ ]?(.*+)[ ]?,[ ]?(.*+)[ ]?\)/for (\1 : \2)/g' $(git grep -l 'BOOST_FOREACH' -- 'src/*.h' 'src/*.cpp' ':!src/leveldb/**' ':!src/univalue/**' ':!src/qt/locale') sed -i -r -e 's/foreach[ ]?\([ ]?(.*+)[ ]?,[ ]?(.*+)[ ]?\)/for (\1 : \2)/g' $(git grep -l 'foreach' -- 'src/*.h' 'src/*.cpp' ':!src/leveldb/**' ':!src/univalue/**' ':!src/qt/locale') sed -i -r -e '/foreach/d' $(git grep -l 'foreach' -- 'src/*.h' 'src/*.cpp' ':!src/leveldb/**' ':!src/univalue/**' ':!src/qt/locale') sed -i -r -e '/define PAIRTYPE/d' $(git grep -l 'PAIRTYPE' -- 'src/*.h' 'src/*.cpp' ':!src/leveldb/**' ':!src/univalue/**' ':!src/qt/locale') sed -i -r -e 's/PAIRTYPE\([ ]?(.*+)[ ]?,[ ]?(.*+)[ ]?\)/std::pair<\1, \2>/g' $(git grep -l 'PAIRTYPE' -- 'src/*.h' 'src/*.cpp' ':!src/leveldb/**' ':!src/univalue/**' ':!src/qt/locale') git diff -U0 | contrib/devtools/clang-format-diff.py -p1 -i -v -END VERIFY SCRIPT- --- src/qt/addressbookpage.cpp | 12 ++++-------- src/qt/bitcoingui.cpp | 3 +-- src/qt/coincontroldialog.cpp | 3 +-- src/qt/optionsdialog.cpp | 3 +-- src/qt/rpcconsole.cpp | 3 +-- src/qt/sendcoinsdialog.cpp | 3 +-- src/qt/trafficgraphwidget.cpp | 4 ++-- src/qt/transactiontablemodel.cpp | 3 +-- src/qt/walletmodel.cpp | 9 +++------ src/rpc/dataacq.cpp | 6 +++--- src/test/accounting_tests.cpp | 4 +--- src/test/checkpoints_tests.cpp | 1 - src/test/getarg_tests.cpp | 3 +-- src/test/multisig_tests.cpp | 4 +--- src/test/rpc_tests.cpp | 1 - src/test/script_p2sh_tests.cpp | 1 - src/test/script_tests.cpp | 7 ++----- src/test/sigopcount_tests.cpp | 1 - src/test/transaction_tests.cpp | 8 ++++---- src/test/util_tests.cpp | 1 - src/util.h | 2 -- 21 files changed, 27 insertions(+), 55 deletions(-) mode change 100755 => 100644 src/test/accounting_tests.cpp mode change 100755 => 100644 src/test/multisig_tests.cpp mode change 100755 => 100644 src/test/script_tests.cpp diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 67012e381f..52109b16f8 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -194,8 +194,7 @@ void AddressBookPage::on_signMessageButton_clicked() QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); QString addr; - foreach (QModelIndex index, indexes) - { + for (QModelIndex index : indexes) { QVariant address = index.data(); addr = address.toString(); } @@ -209,8 +208,7 @@ void AddressBookPage::on_verifyMessageButton_clicked() QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); QString addr; - foreach (QModelIndex index, indexes) - { + for (QModelIndex index : indexes) { QVariant address = index.data(); addr = address.toString(); } @@ -302,8 +300,7 @@ void AddressBookPage::done(int retval) // Figure out which address was selected, and return it QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - foreach (QModelIndex index, indexes) - { + for (QModelIndex index : indexes) { QVariant address = table->model()->data(index); returnValue = address.toString(); } @@ -352,8 +349,7 @@ void AddressBookPage::on_showQRCodeButton_clicked() QTableView *table = ui->tableView; QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - foreach (QModelIndex index, indexes) - { + for (QModelIndex index : indexes) { QString address = index.data().toString(), label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d6776852cc..1c64888fc0 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1354,8 +1354,7 @@ void BitcoinGUI::dropEvent(QDropEvent *event) { int nValidUrisFound = 0; QList uris = event->mimeData()->urls(); - foreach(const QUrl &uri, uris) - { + for (const QUrl& uri : uris) { if (sendCoinsPage->handleURI(uri.toString())) nValidUrisFound++; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 48d0081e00..9f3ae29d47 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -649,8 +649,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, bool fLowOutput = false; bool fDust = false; CTransaction txDummy; - foreach(const qint64 &amount, *payAmounts) - { + for (const qint64& amount : *payAmounts) { nPayAmount += amount; if (amount > 0) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 2d85a2a12f..f66e4b8aef 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -54,8 +54,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) : /* Display elements init */ QDir translations(":translations"); ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); - foreach(const QString &langStr, translations.entryList()) - { + for (const QString& langStr : translations.entryList()) { QLocale locale(langStr); /** check if the locale name consists of 2 parts (language_country) */ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index b79672a1f0..e41fe22c83 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -104,8 +104,7 @@ bool parseCommandLine(std::vector &args, const std::string &strComm STATE_ESCAPE_DOUBLEQUOTED } state = STATE_EATING_SPACES; std::string curarg; - foreach(char ch, strCommand) - { + for (char ch : strCommand) { switch(state) { case STATE_ARGUMENT: // In or after argument diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 2f7d7bed0b..60862736ec 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -150,8 +150,7 @@ void SendCoinsDialog::on_sendButton_clicked() // Format confirmation message QStringList formatted; - foreach(const SendCoinsRecipient &rcp, recipients) - { + for (const SendCoinsRecipient& rcp : recipients) { formatted.append(tr("%1 to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), rcp.label.toHtmlEscaped(), rcp.address)); } diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index d63be2b7e3..7eda0af46b 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -151,10 +151,10 @@ void TrafficGraphWidget::updateRates() } float tmax = 0.0f; - foreach(float f, vSamplesIn) { + for (float f : vSamplesIn) { if(f > tmax) tmax = f; } - foreach(float f, vSamplesOut) { + for (float f : vSamplesOut) { if(f > tmax) tmax = f; } fMax = tmax; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index b66956048c..f620afd263 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -172,8 +172,7 @@ class TransactionTablePriv { parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); int insert_idx = lowerIndex; - foreach(const TransactionRecord &rec, toInsert) - { + for (const TransactionRecord& rec : toInsert) { cachedWallet.insert(insert_idx, rec); insert_idx += 1; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 5e05826052..55194749e7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -184,8 +184,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList > vecSend; - foreach(const SendCoinsRecipient &rcp, recipients) - { + for (const SendCoinsRecipient& rcp : recipients) { CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(std::make_pair(scriptPubKey, rcp.amount)); @@ -268,8 +266,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList list; + std::vector> list; std::copy(c_version.begin(), c_version.end(), back_inserter(list)); std::sort(list.begin(), list.end(), compare_second); @@ -301,7 +301,7 @@ UniValue rpc_getblockstats(const UniValue& params, bool fHelp) // cpids { UniValue result(UniValue::VOBJ); - std::vector list; + std::vector> list; std::copy(c_cpid.begin(), c_cpid.end(), back_inserter(list)); std::sort(list.begin(), list.end(), compare_second); @@ -315,7 +315,7 @@ UniValue rpc_getblockstats(const UniValue& params, bool fHelp) // orgs { UniValue result(UniValue::VOBJ); - std::vector list; + std::vector> list; std::copy(c_org.begin(), c_org.end(), back_inserter(list)); std::sort(list.begin(), list.end(), compare_second); diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp old mode 100755 new mode 100644 index 45518b7ce3..971cf5c853 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -1,6 +1,5 @@ #include -#include #include "init.h" #include "wallet/wallet.h" @@ -18,8 +17,7 @@ GetResults(CWalletDB& walletdb, std::map& results) results.clear(); BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain) == DB_LOAD_OK); walletdb.ListAccountCreditDebit("", aes); - BOOST_FOREACH(CAccountingEntry& ae, aes) - { + for (CAccountingEntry& ae : aes) { results[ae.nOrderPos] = ae; } } diff --git a/src/test/checkpoints_tests.cpp b/src/test/checkpoints_tests.cpp index eec0061179..809aa66afc 100644 --- a/src/test/checkpoints_tests.cpp +++ b/src/test/checkpoints_tests.cpp @@ -3,7 +3,6 @@ // #include // for 'map_list_of()' #include -#include #include "../chainparams.h" #include "../checkpoints.h" diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index ebad94bdcc..30fdcfd16c 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -1,5 +1,4 @@ #include -#include #include #include "util/system.h" @@ -33,7 +32,7 @@ static bool ResetArgs(const std::string& strAddArg, const std::string& strArgIn // Convert to char*: std::vector vecChar; - BOOST_FOREACH(std::string& s, vecArg) + for (std::string& s : vecArg) vecChar.push_back(s.c_str()); std::string error; diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp old mode 100755 new mode 100644 index 0b5302b516..87bbae7963 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -33,8 +32,7 @@ sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, CScript result; result << OP_0; // CHECKMULTISIG bug workaround - BOOST_FOREACH(CKey key, keys) - { + for (CKey key : keys) { vector vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 238c51c1f5..cd832e983b 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -1,5 +1,4 @@ #include -#include #include "base58.h" #include "util.h" diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp index f59fbf3bb0..8cd7573841 100755 --- a/src/test/script_p2sh_tests.cpp +++ b/src/test/script_p2sh_tests.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "main.h" #include "policy/policy.h" diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp old mode 100755 new mode 100644 index 96f2634f30..132bb7bd75 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -49,8 +48,7 @@ ParseScript(string s) vector words; split(words, s, is_any_of(" \t\n"), token_compress_on); - BOOST_FOREACH(string w, words) - { + for (string w : words) { if (w.empty()) { // Empty string, ignore. (boost::split given '' will return one word) @@ -196,8 +194,7 @@ sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transac // and vice-versa) // result << OP_0; - BOOST_FOREACH(CKey key, keys) - { + for (CKey key : keys) { vector vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 9a300611e1..79c119fbb5 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "script.h" #include "key.h" diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index d0cbb1c3a6..b5a8940f7c 100755 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) // ... where all scripts are stringified scripts. Array tests = read_json("tx_valid.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); string strTest = write_string(tv, false); @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) map mapprevOutScriptPubKeys; Array inputs = test[0].get_array(); bool fValid = true; - BOOST_FOREACH(Value& input, inputs) + for (Value& input : inputs) { if (input.type() != array_type) { @@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // ... where all scripts are stringified scripts. Array tests = read_json("tx_invalid.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); string strTest = write_string(tv, false); @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) map mapprevOutScriptPubKeys; Array inputs = test[0].get_array(); bool fValid = true; - BOOST_FOREACH(Value& input, inputs) + for (Value& input : inputs) { if (input.type() != array_type) { diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 606228353c..6e66da2bac 100755 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "main.h" #include "wallet/wallet.h" diff --git a/src/util.h b/src/util.h index bb3777f29e..75f775f9e2 100644 --- a/src/util.h +++ b/src/util.h @@ -59,8 +59,6 @@ #define PRIpdu "tu" #define PRIpdd "td" -// This is needed because the foreach macro can't get over the comma in pair -#define PAIRTYPE(t1, t2) std::pair #ifdef WIN32 #define MSG_NOSIGNAL 0 From 4c4f1f5f2a4624f73e9bdb9591d45c78d8199ab0 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sun, 13 Jun 2021 16:23:01 +0300 Subject: [PATCH 193/280] NULL, 0 -> nullptr --- src/addrman.cpp | 4 +- src/addrman.h | 4 +- src/base58.h | 3 +- src/bignum.h | 26 ++--- src/crypter.cpp | 4 +- src/gridcoin/scraper/http.cpp | 8 +- src/gridcoin/scraper/scraper_net.cpp | 2 +- src/gridcoin/staking/kernel.cpp | 2 +- src/gridcoinresearchd.cpp | 4 +- src/init.cpp | 6 +- src/key.cpp | 126 ++++++++++++++----------- src/main.cpp | 53 +++++------ src/main.h | 10 +- src/miner.cpp | 7 +- src/net.cpp | 68 ++++++------- src/net.h | 10 +- src/netbase.cpp | 13 ++- src/netbase.h | 2 +- src/primitives/transaction.h | 8 +- src/qt/aboutdialog.h | 2 +- src/qt/addressbookpage.cpp | 13 ++- src/qt/addressbookpage.h | 2 +- src/qt/addresstablemodel.cpp | 7 +- src/qt/addresstablemodel.h | 2 +- src/qt/askpassphrasedialog.cpp | 11 +-- src/qt/askpassphrasedialog.h | 2 +- src/qt/bitcoin.cpp | 14 +-- src/qt/bitcoinaddressvalidator.h | 2 +- src/qt/bitcoinamountfield.cpp | 5 +- src/qt/bitcoinamountfield.h | 2 +- src/qt/bitcoingui.cpp | 35 ++++--- src/qt/bitcoingui.h | 2 +- src/qt/clientmodel.cpp | 5 +- src/qt/clientmodel.h | 2 +- src/qt/coincontroldialog.cpp | 13 ++- src/qt/coincontroldialog.h | 6 +- src/qt/coincontroltreewidget.h | 4 +- src/qt/csvmodelwriter.cpp | 5 +- src/qt/csvmodelwriter.h | 2 +- src/qt/diagnosticsdialog.h | 2 +- src/qt/editaddressdialog.cpp | 5 +- src/qt/editaddressdialog.h | 2 +- src/qt/guiutil.cpp | 14 +-- src/qt/guiutil.h | 6 +- src/qt/intro.cpp | 2 +- src/qt/optionsdialog.cpp | 15 ++- src/qt/optionsdialog.h | 2 +- src/qt/optionsmodel.h | 2 +- src/qt/overviewpage.h | 2 +- src/qt/qrcodedialog.cpp | 9 +- src/qt/qrcodedialog.h | 2 +- src/qt/qtipcserver.cpp | 4 +- src/qt/qvalidatedlineedit.h | 2 +- src/qt/qvaluecombobox.h | 2 +- src/qt/rpcconsole.cpp | 3 +- src/qt/rpcconsole.h | 2 +- src/qt/sendcoinsdialog.cpp | 15 ++- src/qt/sendcoinsdialog.h | 2 +- src/qt/sendcoinsentry.cpp | 7 +- src/qt/sendcoinsentry.h | 2 +- src/qt/signverifymessagedialog.cpp | 7 +- src/qt/signverifymessagedialog.h | 2 +- src/qt/trafficgraphwidget.cpp | 19 ++-- src/qt/trafficgraphwidget.h | 2 +- src/qt/transactiondescdialog.h | 2 +- src/qt/transactionfilterproxy.h | 2 +- src/qt/transactionrecord.cpp | 2 +- src/qt/transactiontablemodel.cpp | 2 +- src/qt/transactiontablemodel.h | 2 +- src/qt/transactionview.h | 2 +- src/qt/walletmodel.cpp | 15 ++- src/qt/walletmodel.h | 4 +- src/rpc/blockchain.cpp | 5 +- src/rpc/blockchain.h | 2 +- src/rpc/client.cpp | 2 +- src/rpc/protocol.cpp | 6 +- src/rpc/rawtransaction.cpp | 10 +- src/rpc/server.cpp | 30 +++--- src/script.cpp | 4 +- src/script.h | 4 +- src/sync.h | 2 +- src/test/gridcoin/superblock_tests.cpp | 2 +- src/test/rpc_tests.cpp | 2 +- src/txdb-leveldb.cpp | 24 ++--- src/txdb-leveldb.h | 4 +- src/util.cpp | 4 +- src/util.h | 2 +- src/util/system.cpp | 2 +- src/util/time.cpp | 2 +- src/validation.h | 2 +- src/wallet/db.cpp | 69 +++++++------- src/wallet/db.h | 24 ++--- src/wallet/rpcdump.cpp | 4 +- src/wallet/rpcwallet.cpp | 12 +-- src/wallet/wallet.cpp | 12 +-- src/wallet/wallet.h | 20 ++-- src/wallet/walletdb.cpp | 18 ++-- 97 files changed, 459 insertions(+), 468 deletions(-) mode change 100755 => 100644 src/qt/guiutil.cpp diff --git a/src/addrman.cpp b/src/addrman.cpp index 892d567782..69dcb6e54c 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -82,13 +82,13 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int *pnId) { std::map::iterator it = mapAddr.find(addr); if (it == mapAddr.end()) - return NULL; + return nullptr; if (pnId) *pnId = it->second; std::map::iterator it2 = mapInfo.find(it->second); if (it2 != mapInfo.end()) return &it2->second; - return NULL; + return nullptr; } CAddrInfo* CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId) diff --git a/src/addrman.h b/src/addrman.h index 4e2cf543df..4485e142ed 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -201,11 +201,11 @@ class CAddrMan protected: // Find an entry. - CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL); + CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr); // find an entry, creating it if necessary. // nTime and nServices of found node is updated, if necessary. - CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL); + CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr); // Swap two elements in vRandom. void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); diff --git a/src/base58.h b/src/base58.h index f8279b2c77..9000474b79 100644 --- a/src/base58.h +++ b/src/base58.h @@ -88,8 +88,7 @@ inline bool DecodeBase58(const char* psz, std::vector& vchRet) for (const char* p = psz; *p; p++) { const char* p1 = strchr(pszBase58, *p); - if (p1 == NULL) - { + if (p1 == nullptr) { while (isspace(*p)) p++; if (*p != '\0') diff --git a/src/bignum.h b/src/bignum.h index 80ece0a78f..7724ab5b3f 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -36,20 +36,20 @@ class CAutoBN_CTX CAutoBN_CTX() { pctx = BN_CTX_new(); - if (pctx == NULL) - throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); + if (pctx == nullptr) + throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned nullptr"); } ~CAutoBN_CTX() { - if (pctx != NULL) + if (pctx != nullptr) BN_CTX_free(pctx); } operator BN_CTX*() { return pctx; } BN_CTX& operator*() { return *pctx; } BN_CTX** operator&() { return &pctx; } - bool operator!() { return (pctx == NULL); } + bool operator!() { return (pctx == nullptr); } }; /* RAII wrapper for BIGNUM instance */ @@ -62,8 +62,8 @@ class CBigNumBase CBigNumBase() : pbn(BN_new()) { - if (pbn == NULL) - throw bignum_error("CBigNum : BN_new() returned NULL"); + if (pbn == nullptr) + throw bignum_error("CBigNum : BN_new() returned nullptr"); } ~CBigNumBase() @@ -214,7 +214,7 @@ class CBigNum : public CBigNumBase uint64_t getuint64() { - unsigned int nSize = BN_bn2mpi(pbn, NULL); + unsigned int nSize = BN_bn2mpi(pbn, nullptr); if (nSize < 4) return 0; std::vector vch(nSize); @@ -284,7 +284,7 @@ class CBigNum : public CBigNumBase uint256 getuint256() const { - unsigned int nSize = BN_bn2mpi(pbn, NULL); + unsigned int nSize = BN_bn2mpi(pbn, nullptr); if (nSize < 4) return uint256(); std::vector vch(nSize); @@ -315,7 +315,7 @@ class CBigNum : public CBigNumBase std::vector getvch() const { - unsigned int nSize = BN_bn2mpi(pbn, NULL); + unsigned int nSize = BN_bn2mpi(pbn, nullptr); if (nSize <= 4) return std::vector(); std::vector vch(nSize); @@ -339,7 +339,7 @@ class CBigNum : public CBigNumBase unsigned int GetCompact() const { - unsigned int nSize = BN_bn2mpi(pbn, NULL); + unsigned int nSize = BN_bn2mpi(pbn, nullptr); std::vector vch(nSize); nSize -= 4; BN_bn2mpi(pbn, &vch[0]); @@ -504,7 +504,7 @@ class CBigNum : public CBigNumBase */ static CBigNum generatePrime(const unsigned int numBits, bool safe = false) { CBigNum ret; - if(!BN_generate_prime_ex(&ret, numBits, safe, NULL, NULL, NULL)) + if (!BN_generate_prime_ex(&ret, numBits, safe, nullptr, nullptr, nullptr)) throw bignum_error("CBigNum::generatePrime*= :BN_generate_prime_ex"); return ret; } @@ -530,7 +530,7 @@ class CBigNum : public CBigNumBase */ bool isPrime(const int checks=BN_prime_checks) const { CAutoBN_CTX pctx; - int ret = BN_is_prime_ex(pbn, checks, pctx, NULL); + int ret = BN_is_prime_ex(pbn, checks, pctx, nullptr); if(ret < 0){ throw bignum_error("CBigNum::isPrime :BN_is_prime_ex"); } @@ -688,7 +688,7 @@ inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) { CAutoBN_CTX pctx; CBigNum r; - if (!BN_div(&r, NULL, &a, &b, pctx)) + if (!BN_div(&r, nullptr, &a, &b, pctx)) throw bignum_error("CBigNum::operator/ : BN_div failed"); return r; } diff --git a/src/crypter.cpp b/src/crypter.cpp index 7213bfc3c5..1292d18c71 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -73,7 +73,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector& vchCiphertext, CKeyingM if(!ctx) throw std::runtime_error("Error allocating cipher context"); - if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, vchKey.data(), vchIV.data()); + if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, vchKey.data(), vchIV.data()); if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen); if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0])+nPLen, &nFLen); EVP_CIPHER_CTX_free(ctx); diff --git a/src/gridcoin/scraper/http.cpp b/src/gridcoin/scraper/http.cpp index 485be16995..5cd1d27234 100644 --- a/src/gridcoin/scraper/http.cpp +++ b/src/gridcoin/scraper/http.cpp @@ -224,7 +224,7 @@ std::string Http::GetEtag( const std::string &url, const std::string &userpass) { - struct curl_slist* headers = NULL; + struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, "User-Agent: curl/7.63.0"); std::string header; @@ -272,7 +272,7 @@ std::string Http::GetLatestVersionResponse() std::string header; std::string url = gArgs.GetArg("-updatecheckurl", "https://api.github.com/repos/gridcoin-community/Gridcoin-Research/releases/latest"); - struct curl_slist* headers = NULL; + struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, "User-Agent: curl/7.63.0"); @@ -318,7 +318,7 @@ void Http::DownloadSnapshot() std::string buffer; std::string header; - struct curl_slist* headers = NULL; + struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, "User-Agent: curl/7.63.0"); @@ -385,7 +385,7 @@ std::string Http::GetSnapshotSHA256() std::string header; std::string url = gArgs.GetArg("-snapshotsha256url", "https://snapshot.gridcoin.us/snapshot.zip.sha256"); - struct curl_slist* headers = NULL; + struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, "User-Agent: curl/7.63.0"); diff --git a/src/gridcoin/scraper/scraper_net.cpp b/src/gridcoin/scraper/scraper_net.cpp index 9b72d9aa44..d5d3e88ac8 100644 --- a/src/gridcoin/scraper/scraper_net.cpp +++ b/src/gridcoin/scraper/scraper_net.cpp @@ -126,7 +126,7 @@ int CSplitBlob::addPartData(CDataStream&& vData) /* missing data; use the supplied data */ /* prevent calling the Complete callback FIXME: make this look better */ cntPartsRcvd--; - CSplitBlob::RecvPart(0, vData); + CSplitBlob::RecvPart(nullptr, vData); cntPartsRcvd++; } return n; diff --git a/src/gridcoin/staking/kernel.cpp b/src/gridcoin/staking/kernel.cpp index 4fcf3c37fb..768d34d85b 100644 --- a/src/gridcoin/staking/kernel.cpp +++ b/src/gridcoin/staking/kernel.cpp @@ -222,7 +222,7 @@ static bool SelectBlockFromCandidates( { bool fSelected = false; arith_uint256 hashBest = 0; - *pindexSelected = (const CBlockIndex*) 0; + *pindexSelected = nullptr; for (auto const& item : vSortedByTimestamp) { diff --git a/src/gridcoinresearchd.cpp b/src/gridcoinresearchd.cpp index c9b7e8bcf9..167f794a91 100644 --- a/src/gridcoinresearchd.cpp +++ b/src/gridcoinresearchd.cpp @@ -209,7 +209,7 @@ bool AppInit(int argc, char* argv[]) } catch (...) { LogPrintf("AppInit()Exception2"); - PrintException(NULL, "AppInit()"); + PrintException(nullptr, "AppInit()"); } if(fRet) { @@ -217,7 +217,7 @@ bool AppInit(int argc, char* argv[]) MilliSleep(500); } - Shutdown(NULL); + Shutdown(nullptr); // delete thread handler threads->interruptAll(); diff --git a/src/init.cpp b/src/init.cpp index 331237973e..ecb4c42890 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -716,7 +716,7 @@ bool AppInit2(ThreadHandlerPtr threads) #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); + _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)); #endif #if _MSC_VER >= 1400 // Disable confusing "helpful" text message on abort, Ctrl-C @@ -733,7 +733,7 @@ bool AppInit2(ThreadHandlerPtr threads) #endif typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); - if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); + if (setProcDEPPol != nullptr) setProcDEPPol(PROCESS_DEP_ENABLE); #endif #ifndef WIN32 umask(077); @@ -1368,7 +1368,7 @@ bool AppInit2(ThreadHandlerPtr threads) LogPrintf("mapAddressBook.size() = %" PRIszu, pwalletMain->mapAddressBook.size()); } - if (!threads->createThread(StartNode, NULL, "Start Thread")) + if (!threads->createThread(StartNode, nullptr, "Start Thread")) InitError(_("Error: could not start node")); if (fServer) StartRPCThreads(); diff --git a/src/key.cpp b/src/key.cpp index 35b1010f34..1b534a8a90 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -16,7 +16,7 @@ #if OPENSSL_VERSION_NUMBER < 0x10100000L int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) { - if (r == NULL || s == NULL) + if (r == nullptr || s == nullptr) return 0; BN_clear_free(sig->r); BN_clear_free(sig->s); @@ -27,9 +27,9 @@ int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) { - if (pr != NULL) + if (pr != nullptr) *pr = sig->r; - if (ps != NULL) + if (ps != nullptr) *ps = sig->s; } #endif @@ -38,22 +38,22 @@ void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { int ok = 0; - BN_CTX *ctx = NULL; - EC_POINT *pub_key = NULL; + BN_CTX* ctx = nullptr; + EC_POINT* pub_key = nullptr; if (!eckey) return 0; const EC_GROUP *group = EC_KEY_get0_group(eckey); - if ((ctx = BN_CTX_new()) == NULL) + if ((ctx = BN_CTX_new()) == nullptr) goto err; pub_key = EC_POINT_new(group); - if (pub_key == NULL) + if (pub_key == nullptr) goto err; - if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + if (!EC_POINT_mul(group, pub_key, priv_key, nullptr, nullptr, ctx)) goto err; EC_KEY_set_private_key(eckey,priv_key); @@ -65,7 +65,7 @@ int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) if (pub_key) EC_POINT_free(pub_key); - if (ctx != NULL) + if (ctx != nullptr) BN_CTX_free(ctx); return(ok); @@ -79,26 +79,29 @@ int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned ch if (!eckey) return 0; int ret = 0; - BN_CTX *ctx = NULL; - - BIGNUM *x = NULL; - BIGNUM *e = NULL; - BIGNUM *order = NULL; - BIGNUM *sor = NULL; - BIGNUM *eor = NULL; - BIGNUM *field = NULL; - EC_POINT *R = NULL; - EC_POINT *O = NULL; - EC_POINT *Q = NULL; - BIGNUM *rr = NULL; - BIGNUM *zero = NULL; + BN_CTX* ctx = nullptr; + + BIGNUM* x = nullptr; + BIGNUM* e = nullptr; + BIGNUM* order = nullptr; + BIGNUM* sor = nullptr; + BIGNUM* eor = nullptr; + BIGNUM* field = nullptr; + EC_POINT* R = nullptr; + EC_POINT* O = nullptr; + EC_POINT* Q = nullptr; + BIGNUM* rr = nullptr; + BIGNUM* zero = nullptr; int n = 0; int i = recid / 2; const BIGNUM *pr, *ps; ECDSA_SIG_get0(ecsig, &pr, &ps); const EC_GROUP *group = EC_KEY_get0_group(eckey); - if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } + if ((ctx = BN_CTX_new()) == nullptr) { + ret = -1; + goto err; + } BN_CTX_start(ctx); order = BN_CTX_get(ctx); if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } @@ -107,17 +110,32 @@ int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned ch if (!BN_mul_word(x, i)) { ret=-1; goto err; } if (!BN_add(x, x, pr)) { ret=-1; goto err; } field = BN_CTX_get(ctx); - if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } + if (!EC_GROUP_get_curve_GFp(group, field, nullptr, nullptr, ctx)) { + ret = -2; + goto err; + } if (BN_cmp(x, field) >= 0) { ret=0; goto err; } - if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if ((R = EC_POINT_new(group)) == nullptr) { + ret = -2; + goto err; + } if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } if (check) { - if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } + if ((O = EC_POINT_new(group)) == nullptr) { + ret = -2; + goto err; + } + if (!EC_POINT_mul(group, O, nullptr, R, order, ctx)) { + ret = -2; + goto err; + } if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } } - if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if ((Q = EC_POINT_new(group)) == nullptr) { + ret = -2; + goto err; + } n = EC_GROUP_get_degree(group); e = BN_CTX_get(ctx); if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } @@ -141,9 +159,9 @@ int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned ch BN_CTX_end(ctx); BN_CTX_free(ctx); } - if (R != NULL) EC_POINT_free(R); - if (O != NULL) EC_POINT_free(O); - if (Q != NULL) EC_POINT_free(Q); + if (R != nullptr) EC_POINT_free(R); + if (O != nullptr) EC_POINT_free(O); + if (Q != nullptr) EC_POINT_free(Q); return ret; } @@ -156,24 +174,24 @@ void CKey::SetCompressedPubKey() void CKey::Reset() { fCompressedPubKey = false; - if (pkey != NULL) + if (pkey != nullptr) EC_KEY_free(pkey); pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) + if (pkey == nullptr) throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); fSet = false; } CKey::CKey() { - pkey = NULL; + pkey = nullptr; Reset(); } CKey::CKey(const CKey& b) { pkey = EC_KEY_dup(b.pkey); - if (pkey == NULL) + if (pkey == nullptr) throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); fSet = b.fSet; } @@ -274,9 +292,9 @@ bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) } // If vchPrivKey data is bad d2i_ECPrivateKey() can // leave pkey in a state where calling EC_KEY_free() - // crashes. To avoid that, set pkey to NULL and + // crashes. To avoid that, set pkey to nullptr and // leak the memory (a leak is better than a crash) - pkey = NULL; + pkey = nullptr; Reset(); return false; } @@ -285,12 +303,12 @@ bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) { EC_KEY_free(pkey); pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if (pkey == NULL) + if (pkey == nullptr) throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed"); if (vchSecret.size() != 32) throw key_error("CKey::SetSecret() : secret must be 32 bytes"); BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); - if (bn == NULL) + if (bn == nullptr) throw key_error("CKey::SetSecret() : BN_bin2bn failed"); if (!EC_KEY_regenerate_key(pkey,bn)) { @@ -310,7 +328,7 @@ CSecret CKey::GetSecret(bool &fCompressed) const vchRet.resize(32); const BIGNUM *bn = EC_KEY_get0_private_key(pkey); int nBytes = BN_num_bytes(bn); - if (bn == NULL) + if (bn == nullptr) throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed"); int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); if (n != nBytes) @@ -321,7 +339,7 @@ CSecret CKey::GetSecret(bool &fCompressed) const CPrivKey CKey::GetPrivKey() const { - int nSize = i2d_ECPrivateKey(pkey, NULL); + int nSize = i2d_ECPrivateKey(pkey, nullptr); if (!nSize) throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); CPrivKey vchPrivKey(nSize, 0); @@ -341,14 +359,14 @@ bool CKey::SetPubKey(const CPubKey& vchPubKey) SetCompressedPubKey(); return true; } - pkey = NULL; + pkey = nullptr; Reset(); return false; } CPubKey CKey::GetPubKey() const { - int nSize = i2o_ECPublicKey(pkey, NULL); + int nSize = i2o_ECPublicKey(pkey, nullptr); if (!nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); std::vector vchPubKey(nSize, 0); @@ -362,7 +380,7 @@ bool CKey::Sign(uint256 hash, std::vector& vchSig) { vchSig.clear(); ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig == NULL) + if (sig == nullptr) return false; BN_CTX *ctx = BN_CTX_new(); BN_CTX_start(ctx); @@ -378,12 +396,12 @@ bool CKey::Sign(uint256 hash, std::vector& vchSig) // enforce low S values, by negating the value (modulo the order) if above order/2. BIGNUM *nps = BN_dup(ps); if(!nps) - throw std::runtime_error("CKey : BN_dup() returned NULL"); + throw std::runtime_error("CKey : BN_dup() returned nullptr"); BIGNUM *npr = BN_dup(pr); if(!npr) { BN_free(nps); - throw std::runtime_error("CKey : BN_dup() returned NULL"); + throw std::runtime_error("CKey : BN_dup() returned nullptr"); } BN_sub(nps, order, nps); @@ -408,7 +426,7 @@ bool CKey::SignCompact(uint256 hash, std::vector& vchSig) { bool fOk = false; ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig==NULL) + if (sig == nullptr) return false; vchSig.clear(); vchSig.resize(65,0); @@ -457,12 +475,12 @@ bool CKey::ReserealizeSignature(std::vector& vchSig) { return false; pos = &vchSig[0]; - ECDSA_SIG *sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&pos, vchSig.size()); - if (sig == NULL) + ECDSA_SIG* sig = d2i_ECDSA_SIG(nullptr, (const unsigned char**)&pos, vchSig.size()); + if (sig == nullptr) return false; bool ret = false; - int nSize = i2d_ECDSA_SIG(sig, NULL); + int nSize = i2d_ECDSA_SIG(sig, nullptr); if (nSize > 0) { vchSig.resize(nSize); // grow or shrink as needed @@ -492,8 +510,8 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector& v ECDSA_SIG *sig = ECDSA_SIG_new(); ECDSA_SIG_set0( sig, - BN_bin2bn(&vchSig[1],32,NULL), - BN_bin2bn(&vchSig[33],32,NULL)); + BN_bin2bn(&vchSig[1], 32, nullptr), + BN_bin2bn(&vchSig[33], 32, nullptr)); EC_KEY_free(pkey); pkey = EC_KEY_new_by_curve_name(NID_secp256k1); @@ -517,7 +535,7 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector& v { // BIP66 Compatibility: // New versions of OpenSSL (1.0.0p+ and 1.0.1k+) will reject non-canonical DER signatures. de/re-serialize first. - unsigned char *norm_der = NULL; + unsigned char* norm_der = nullptr; ECDSA_SIG *norm_sig = ECDSA_SIG_new(); const unsigned char* sigptr = &vchSig[0]; d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()); @@ -550,7 +568,7 @@ bool CKey::IsValid() bool ECC_InitSanityCheck() { EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if(pkey == NULL) + if (pkey == nullptr) return false; EC_KEY_free(pkey); diff --git a/src/main.cpp b/src/main.cpp index d6b95824c0..90b86de769 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,11 +82,11 @@ unsigned int nStakeMaxAge = -1; // unlimited // Gridcoin: int nCoinbaseMaturity = 100; -CBlockIndex* pindexGenesisBlock = NULL; +CBlockIndex* pindexGenesisBlock = nullptr; int nBestHeight = -1; uint256 hashBestChain; -CBlockIndex* pindexBest = NULL; +CBlockIndex* pindexBest = nullptr; std::atomic g_previous_block_time; std::atomic g_nTimeBestReceived; CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -446,8 +446,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) AssertLockHeld(cs_main); CBlock blockTmp; - if (pblock == NULL) - { + if (pblock == nullptr) { // Load the block this tx is in CTxIndex txindex; if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) @@ -522,7 +521,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInput return false; // Check for conflicts with in-memory transactions - CTransaction* ptxOld = NULL; + CTransaction* ptxOld = nullptr; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -759,7 +758,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool() { - return ::AcceptToMemoryPool(mempool, *this, NULL); + return ::AcceptToMemoryPool(mempool, *this, nullptr); } @@ -871,7 +870,7 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { LOCK(cs_main); - if ((pindexBest == NULL || nBestHeight < GetNumBlocksOfPeers()) && nBestHeight<1185000) + if ((pindexBest == nullptr || nBestHeight < GetNumBlocksOfPeers()) && nBestHeight < 1185000) return true; static int64_t nLastUpdate; static CBlockIndex* pindexLastBest; @@ -1020,7 +1019,7 @@ int64_t ReturnCurrentMoneySupply(CBlockIndex* pindexcurrent) { return (pindexcurrent->pprev? pindexcurrent->pprev->nMoneySupply : 0); } - // At this point, either the last block pointer was NULL, or the client erased the money supply previously, fix it: + // At this point, either the last block pointer was nullptr, or the client erased the money supply previously, fix it: CBlockIndex* pblockIndex = pindexcurrent; CBlockIndex* pblockMemory = pindexcurrent; int nMinDepth = (pindexcurrent->nHeight)-140000; @@ -1030,7 +1029,7 @@ int64_t ReturnCurrentMoneySupply(CBlockIndex* pindexcurrent) pblockIndex = pblockIndex->pprev; LogPrintf("Money Supply height %d", pblockIndex->nHeight); - if (pblockIndex == NULL || !pblockIndex->IsInMainChain()) continue; + if (pblockIndex == nullptr || !pblockIndex->IsInMainChain()) continue; if (pblockIndex == pindexGenesisBlock) { return nGenesisSupply; @@ -1729,7 +1728,7 @@ bool DisconnectBlocksBatch(CTxDB& txdb, list& vResurrect, unsigned // disconnect from memory assert(!pindexBest->pnext); if (pindexBest->pprev) - pindexBest->pprev->pnext = NULL; + pindexBest->pprev->pnext = nullptr; // Queue memory transactions to resurrect. // We only do this for blocks after the last checkpoint (reorganisation before that @@ -1802,7 +1801,7 @@ bool DisconnectBlocksBatch(CTxDB& txdb, list& vResurrect, unsigned { // Resurrect memory transactions that were in the disconnected branch for( CTransaction& tx : vResurrect) - AcceptToMemoryPool(mempool, tx, NULL); + AcceptToMemoryPool(mempool, tx, nullptr); if (!txdb.TxnCommit()) return error("DisconnectBlocksBatch: TxnCommit failed"); /*fatal*/ @@ -1840,12 +1839,11 @@ bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock & set vRereadCPIDs; /* find fork point */ - CBlockIndex *pcommon = NULL; + CBlockIndex* pcommon = nullptr; if(pindexGenesisBlock) { pcommon = pindexNew; - while( pcommon->pnext==NULL && pcommon!=pindexBest ) - { + while (pcommon->pnext == nullptr && pcommon != pindexBest) { pcommon = pcommon->pprev; if(!pcommon) @@ -1925,17 +1923,14 @@ bool ReorganizeChain(CTxDB& txdb, unsigned &cnt_dis, unsigned &cnt_con, CBlock & if (!txdb.TxnBegin()) return error("ReorganizeChain: TxnBegin failed"); - if (pindexGenesisBlock == NULL) - { + if (pindexGenesisBlock == nullptr) { if(hash != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) { txdb.TxnAbort(); return error("ReorganizeChain: genesis block hash does not match"); } pindexGenesisBlock = pindex; - } - else - { + } else { assert(pindex->GetBlockHash()==block.GetHash(true)); assert(pindex->pprev == pindexBest); if (!block.ConnectBlock(txdb, pindex, false)) @@ -2558,8 +2553,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, bool generated_by_me) { // Extra checks to prevent "fill up memory by spamming with bogus blocks" const CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if(pcheckpoint != NULL) - { + if (pcheckpoint != nullptr) { int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { @@ -2708,16 +2702,16 @@ static fs::path BlockFilePath(unsigned int nFile) FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) { if ((nFile < 1) || (nFile == (unsigned int) -1)) - return NULL; + return nullptr; FILE* file = fsbridge::fopen(BlockFilePath(nFile), pszMode); if (!file) - return NULL; + return nullptr; if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) { if (fseek(file, nBlockPos, SEEK_SET) != 0) { fclose(file); - return NULL; + return nullptr; } } return file; @@ -2732,9 +2726,9 @@ FILE* AppendBlockFile(unsigned int& nFileRet) { FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); if (!file) - return NULL; + return nullptr; if (fseek(file, 0, SEEK_END) != 0) - return NULL; + return nullptr; // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB if (ftell(file) < (long)(0x7F000000 - MAX_SIZE)) { @@ -2990,8 +2984,7 @@ bool LoadExternalBlockFile(FILE* fileIn) { CBlock block; blkdat >> block; - if (ProcessBlock(NULL,&block,false)) - { + if (ProcessBlock(nullptr, &block, false)) { nLoaded++; LogPrintf("Blocks/s: %f", nLoaded / ((GetTimeMillis() - nStart) / 1000.0)); nPos += 4 + nSize; @@ -3560,7 +3553,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); - CBlockIndex* pindex = NULL; + CBlockIndex* pindex = nullptr; if (locator.IsNull()) { // If locator is null, return the hashStop block @@ -3966,7 +3959,7 @@ bool ProcessMessages(CNode* pfrom) catch (std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessages()"); + PrintExceptionContinue(nullptr, "ProcessMessages()"); } if (!fRet) diff --git a/src/main.h b/src/main.h index 7f2351561f..8453994c0a 100644 --- a/src/main.h +++ b/src/main.h @@ -186,7 +186,7 @@ class CTxIndex; void RegisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn); -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true); +void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = nullptr, bool fUpdate = false, bool fConnect = true); bool ProcessBlock(CNode* pfrom, CBlock* pblock, bool Generated_By_Me); bool CheckDiskSpace(uint64_t nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); @@ -251,7 +251,7 @@ class CMerkleTx : public CTransaction READWRITE(nIndex); } - int SetMerkleBranch(const CBlock* pblock=NULL); + int SetMerkleBranch(const CBlock* pblock = nullptr); // Return depth of transaction in blockchain: // -1 : not in blockchain, and not in memory pool (conflicted transaction) @@ -645,9 +645,9 @@ class CBlockIndex void SetNull() { - phashBlock = NULL; - pprev = NULL; - pnext = NULL; + phashBlock = nullptr; + pprev = nullptr; + pnext = nullptr; nFile = 0; nBlockPos = 0; nHeight = 0; diff --git a/src/miner.cpp b/src/miner.cpp index 368b71f47c..1685c43641 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -323,7 +323,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) continue; } - COrphan* porphan = NULL; + COrphan* porphan = nullptr; double dPriority = 0; int64_t nTotalIn = 0; bool fMissingInputs = false; @@ -347,7 +347,7 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE)) { - assert("mempool transaction missing input" == 0); + assert("mempool transaction missing input" == nullptr); } fMissingInputs = true; @@ -1425,8 +1425,7 @@ void StakeMiner(CWallet *pwallet) } // * delegate to ProcessBlock - if (!ProcessBlock(NULL, &StakeBlock, true)) - { + if (!ProcessBlock(nullptr, &StakeBlock, true)) { error("StakeMiner: Block vehemently rejected"); continue; } diff --git a/src/net.cpp b/src/net.cpp index 88bedb2c72..e08562659c 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -50,7 +50,7 @@ void ThreadOpenAddedConnections2(void* parg); void ThreadMapPort2(void* parg); #endif void ThreadDNSAddressSeed2(void* parg); -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant* grantOutbound = nullptr, const char* strDest = nullptr, bool fOneShot = false); void StakeMiner(CWallet *pwallet); // @@ -62,7 +62,7 @@ ServiceFlags nLocalServices = NODE_NETWORK; CCriticalSection cs_mapLocalHost; std::map mapLocalHost; static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {}; -static CNode* pnodeLocalHost = NULL; +static CNode* pnodeLocalHost = nullptr; CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices); uint64_t nLocalHostNonce = 0; @@ -96,7 +96,7 @@ CCriticalSection cs_setservAddNodeAddresses; std::map> CNode::mapMisbehavior; CCriticalSection CNode::cs_mapMisbehavior; -static CSemaphore *semOutbound = NULL; +static CSemaphore* semOutbound = nullptr; // This caches the block locators used to ask for a range of blocks. Due to a // sub-optimal workaround in our old net messaging code, a node will ask each @@ -410,7 +410,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) } else { - return NULL; + return nullptr; } } @@ -1135,7 +1135,7 @@ void ThreadMapPort(void* parg) } catch (...) { - PrintException(NULL, "ThreadMapPort()"); + PrintException(nullptr, "ThreadMapPort()"); } LogPrintf("ThreadMapPort exited"); } @@ -1145,9 +1145,9 @@ void ThreadMapPort2(void* parg) LogPrint(BCLog::LogFlags::NET, "ThreadMapPort started"); std::string port = strprintf("%u", GetListenPort()); - const char * multicastif = 0; - const char * minissdpdpath = 0; - struct UPNPDev * devlist = 0; + const char* multicastif = nullptr; + const char* minissdpdpath = nullptr; + struct UPNPDev* devlist = nullptr; char lanaddr[64]; int error = 0; @@ -1184,7 +1184,7 @@ void ThreadMapPort2(void* parg) string strDesc = "Gridcoin " + FormatFullVersion(); r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0"); if(r!=UPNPCOMMAND_SUCCESS) LogPrintf("AddPortMapping(%s, %s, %s) unsuccessful with code %d (%s)", @@ -1196,9 +1196,10 @@ void ThreadMapPort2(void* parg) { if (fShutdown || !fUseUPnP) { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr); LogPrintf("UPNP_DeletePortMapping() returned : %d", r); - freeUPNPDevlist(devlist); devlist = 0; + freeUPNPDevlist(devlist); + devlist = nullptr; FreeUPNPUrls(&urls); return; } @@ -1211,7 +1212,7 @@ void ThreadMapPort2(void* parg) #else /* miniupnpc 1.6 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0"); #endif if(r!=UPNPCOMMAND_SUCCESS) @@ -1225,7 +1226,8 @@ void ThreadMapPort2(void* parg) } } else { LogPrint(BCLog::LogFlags::NET, "No valid UPnP IGDs found"); - freeUPNPDevlist(devlist); devlist = 0; + freeUPNPDevlist(devlist); + devlist = nullptr; if (r != 0) FreeUPNPUrls(&urls); while (true) @@ -1241,7 +1243,7 @@ void MapPort() { if (fUseUPnP && !netThreads->threadExists("ThreadMapPort")) { - if (!netThreads->createThread(ThreadMapPort,NULL,"ThreadMapPort")) + if (!netThreads->createThread(ThreadMapPort, nullptr, "ThreadMapPort")) LogPrintf("Error: createThread(ThreadMapPort) failed"); } } @@ -1381,7 +1383,7 @@ void ThreadDumpAddress(void* parg) } catch (...) { - PrintException(NULL, "ThreadDumpAddress"); + PrintException(nullptr, "ThreadDumpAddress"); } LogPrintf("ThreadDumpAddress exited"); } @@ -1406,7 +1408,7 @@ void ThreadOpenConnections(void* parg) } catch (...) { - PrintException(NULL, "ThreadOpenConnections()"); + PrintException(nullptr, "ThreadOpenConnections()"); } LogPrintf("ThreadOpenConnections exited"); } @@ -1449,7 +1451,7 @@ void static ThreadStakeMiner(void* parg) } catch (...) { - PrintException(NULL, "ThreadStakeMiner()"); + PrintException(nullptr, "ThreadStakeMiner()"); } LogPrintf("ThreadStakeMiner exited"); } @@ -1487,7 +1489,7 @@ void ThreadOpenConnections2(void* parg) for (auto const& strAddr : gArgs.GetArgs("-connect")) { CAddress addr; - OpenNetworkConnection(addr, NULL, strAddr.c_str()); + OpenNetworkConnection(addr, nullptr, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { MilliSleep(500); @@ -1611,7 +1613,7 @@ void ThreadOpenAddedConnections(void* parg) } catch (...) { - PrintException(NULL, "ThreadOpenAddedConnections()"); + PrintException(nullptr, "ThreadOpenAddedConnections()"); } LogPrintf("ThreadOpenAddedConnections exited"); } @@ -1733,7 +1735,7 @@ void ThreadMessageHandler(void* parg) } catch (...) { - PrintException(NULL, "ThreadMessageHandler()"); + PrintException(nullptr, "ThreadMessageHandler()"); } LogPrintf("ThreadMessageHandler exited"); } @@ -1752,7 +1754,7 @@ void ThreadMessageHandler2(void* parg) } // Poll the connected nodes for messages - CNode* pnodeTrickle = NULL; + CNode* pnodeTrickle = nullptr; if (!vNodesCopy.empty()) pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; for (auto const& pnode : vNodesCopy) @@ -1952,9 +1954,8 @@ void static Discover() struct ifaddrs* myaddrs; if (getifaddrs(&myaddrs) == 0) { - for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr == NULL) continue; + for (struct ifaddrs* ifa = myaddrs; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) continue; if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (strcmp(ifa->ifa_name, "lo") == 0) continue; if (strcmp(ifa->ifa_name, "lo0") == 0) continue; @@ -1985,7 +1986,7 @@ void StartNode(void* parg) fShutdown = false; MAX_OUTBOUND_CONNECTIONS = (int)gArgs.GetArg("-maxoutboundconnections", 8); int nMaxOutbound = 0; - if (semOutbound == NULL) { + if (semOutbound == nullptr) { // initialize semaphore nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)gArgs.GetArg("-maxconnections", 125)); semOutbound = new CSemaphore(nMaxOutbound); @@ -1993,7 +1994,7 @@ void StartNode(void* parg) LogPrintf("Using %i OutboundConnections with a MaxConnections of %" PRId64, MAX_OUTBOUND_CONNECTIONS, gArgs.GetArg("-maxconnections", 125)); - if (pnodeLocalHost == NULL) + if (pnodeLocalHost == nullptr) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); Discover(); @@ -2004,32 +2005,31 @@ void StartNode(void* parg) if (!gArgs.GetBoolArg("-dnsseed", true)) LogPrintf("DNS seeding disabled"); - else - if (!netThreads->createThread(ThreadDNSAddressSeed,NULL,"ThreadDNSAddressSeed")) - LogPrintf("Error: createThread(ThreadDNSAddressSeed) failed"); + else if (!netThreads->createThread(ThreadDNSAddressSeed, nullptr, "ThreadDNSAddressSeed")) + LogPrintf("Error: createThread(ThreadDNSAddressSeed) failed"); // Map ports with UPnP if (fUseUPnP) MapPort(); // Send and receive from sockets, accept connections - if (!netThreads->createThread(ThreadSocketHandler,NULL,"ThreadSocketHandler")) + if (!netThreads->createThread(ThreadSocketHandler, nullptr, "ThreadSocketHandler")) LogPrintf("Error: createThread(ThreadSocketHandler) failed"); // Initiate outbound connections from -addnode - if (!netThreads->createThread(ThreadOpenAddedConnections,NULL,"ThreadOpenAddedConnections")) + if (!netThreads->createThread(ThreadOpenAddedConnections, nullptr, "ThreadOpenAddedConnections")) LogPrintf("Error: createThread(ThreadOpenAddedConnections) failed"); // Initiate outbound connections - if (!netThreads->createThread(ThreadOpenConnections,NULL,"ThreadOpenConnections")) + if (!netThreads->createThread(ThreadOpenConnections, nullptr, "ThreadOpenConnections")) LogPrintf("Error: createThread(ThreadOpenConnections) failed"); // Process messages - if (!netThreads->createThread(ThreadMessageHandler,NULL,"ThreadMessageHandler")) + if (!netThreads->createThread(ThreadMessageHandler, nullptr, "ThreadMessageHandler")) LogPrintf("Error: createThread(ThreadMessageHandler) failed"); // Dump network addresses - if (!netThreads->createThread(ThreadDumpAddress,NULL,"ThreadDumpAddress")) + if (!netThreads->createThread(ThreadDumpAddress, nullptr, "ThreadDumpAddress")) LogPrintf("Error: createThread(ThreadDumpAddress) failed"); // Mine proof-of-stake blocks in the background diff --git a/src/net.h b/src/net.h index 1849d9985a..a8d8209f94 100644 --- a/src/net.h +++ b/src/net.h @@ -45,7 +45,7 @@ bool GetMyExternalIP(CNetAddr& ipRet); void AddressCurrentlyConnected(const CService& addr); CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CService& ip); -CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL); +CNode* ConnectNode(CAddress addrConnect, const char* strDest = nullptr); void MapPort(); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); @@ -81,10 +81,10 @@ bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); -bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); +bool GetLocal(CService& addr, const CNetAddr* paddrPeer = nullptr); bool IsReachable(const CNetAddr &addr); void SetReachable(enum Network net, bool fFlag = false); -CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); +CAddress GetLocalAddress(const CNetAddr* paddrPeer = nullptr); void AdvertiseLocal(CNode *pnode = nullptr); enum @@ -103,7 +103,7 @@ class CRequestTracker void (*fn)(void*, CDataStream&); void* param1; - explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) + explicit CRequestTracker(void (*fnIn)(void*, CDataStream&) = nullptr, void* param1In = nullptr) { fn = fnIn; param1 = param1In; @@ -111,7 +111,7 @@ class CRequestTracker bool IsNull() { - return fn == NULL; + return fn == nullptr; } }; diff --git a/src/netbase.cpp b/src/netbase.cpp index 14331ec04b..d13466fa00 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -61,14 +61,13 @@ bool static LookupIntern(const char *pszName, std::vector& vIP, unsign #else aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; #endif - struct addrinfo *aiRes = NULL; - int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); + struct addrinfo* aiRes = nullptr; + int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes); if (nErr) return false; struct addrinfo *aiTrav = aiRes; - while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) - { + while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) { if (aiTrav->ai_family == AF_INET) { assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); @@ -366,7 +365,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe fd_set fdset; FD_ZERO(&fdset); FD_SET(hSocket, &fdset); - int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); + int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout); if (nRet == 0) { closesocket(hSocket); @@ -789,7 +788,7 @@ std::string CNetAddr::ToStringIP() const socklen_t socklen = sizeof(sockaddr); if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) { char name[1025] = ""; - if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST)) + if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), nullptr, 0, NI_NUMERICHOST)) return std::string(name); } if (IsIPv4()) @@ -930,7 +929,7 @@ static const int NET_UNKNOWN = NET_MAX + 0; static const int NET_TEREDO = NET_MAX + 1; int static GetExtNetwork(const CNetAddr *addr) { - if (addr == NULL) + if (addr == nullptr) return NET_UNKNOWN; if (addr->IsRFC4380()) return NET_TEREDO; diff --git a/src/netbase.h b/src/netbase.h index 1abd16e685..42e73b7a4c 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -70,7 +70,7 @@ class CNetAddr uint64_t GetHash() const; bool GetInAddr(struct in_addr* pipv4Addr) const; std::vector GetGroup() const; - int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; + int GetReachabilityFrom(const CNetAddr* paddrPartner = nullptr) const; void print() const; CNetAddr(const struct in6_addr& pipv6Addr); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index f14729a9ff..592760e72d 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -20,8 +20,12 @@ class CInPoint CInPoint() { SetNull(); } CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = (unsigned int) -1; } - bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } + void SetNull() + { + ptx = nullptr; + n = (unsigned int)-1; + } + bool IsNull() const { return (ptx == nullptr && n == (unsigned int)-1); } }; diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h index 2ed9e9e7c4..a6e12ce4ec 100644 --- a/src/qt/aboutdialog.h +++ b/src/qt/aboutdialog.h @@ -14,7 +14,7 @@ class AboutDialog : public QDialog Q_OBJECT public: - explicit AboutDialog(QWidget *parent = 0); + explicit AboutDialog(QWidget* parent = nullptr); ~AboutDialog(); void setModel(ClientModel *model); diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 52109b16f8..118dc23c79 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -17,13 +17,12 @@ #include "qrcodedialog.h" #endif -AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : - QDialog(parent), - ui(new Ui::AddressBookPage), - model(0), - optionsModel(0), - mode(mode), - tab(tab) +AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget* parent) : QDialog(parent), + ui(new Ui::AddressBookPage), + model(nullptr), + optionsModel(nullptr), + mode(mode), + tab(tab) { ui->setupUi(this); diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 64c900542e..c95fd3c4c4 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -34,7 +34,7 @@ class AddressBookPage : public QDialog ForEditing /**< Open address book for editing */ }; - explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent = 0); + explicit AddressBookPage(Mode mode, Tabs tab, QWidget* parent = nullptr); ~AddressBookPage(); void setModel(AddressTableModel *model); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index eec0d1ce11..7f02c614df 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -134,13 +134,12 @@ class AddressTablePriv } else { - return 0; + return nullptr; } } }; -AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : - QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) +AddressTableModel::AddressTableModel(CWallet* wallet, WalletModel* parent) : QAbstractTableModel(parent), walletModel(parent), wallet(wallet), priv(nullptr) { columns << tr("Label") << tr("Address"); priv = new AddressTablePriv(wallet, this); @@ -285,7 +284,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const { if(!index.isValid()) - return 0; + return nullptr; AddressTableEntry *rec = static_cast(index.internalPointer()); Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index b7dafa39d3..8616460e5f 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -17,7 +17,7 @@ class AddressTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0); + explicit AddressTableModel(CWallet* wallet, WalletModel* parent = nullptr); ~AddressTableModel(); enum ColumnIndex { diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index c8149d15dd..ea8d9bbb83 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -10,12 +10,11 @@ extern bool fWalletUnlockStakingOnly; -AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : - QDialog(parent), - ui(new Ui::AskPassphraseDialog), - mode(mode), - model(0), - fCapsLock(false) +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget* parent) : QDialog(parent), + ui(new Ui::AskPassphraseDialog), + mode(mode), + model(nullptr), + fCapsLock(false) { ui->setupUi(this); ui->oldPassphraseEdit->setMaxLength(MAX_PASSPHRASE_SIZE); diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index f604ffe142..32b9cf07f0 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -24,7 +24,7 @@ class AskPassphraseDialog : public QDialog Decrypt /**< Ask passphrase and decrypt wallet */ }; - explicit AskPassphraseDialog(Mode mode, QWidget *parent = 0); + explicit AskPassphraseDialog(Mode mode, QWidget* parent = nullptr); ~AskPassphraseDialog(); void accept(); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 99e6884130..3d94da38f1 100755 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -245,7 +245,7 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons static void handleRunawayException(std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); - QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Gridcoin can no longer continue safely and will quit.") + QString("\n") + QString::fromStdString(strMiscWarning)); + QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Gridcoin can no longer continue safely and will quit.") + QString("\n") + QString::fromStdString(strMiscWarning)); exit(1); } @@ -679,14 +679,14 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt app.exec(); window.hide(); - window.setClientModel(0); - window.setWalletModel(0); - window.setResearcherModel(0); - guiref = 0; + window.setClientModel(nullptr); + window.setWalletModel(nullptr); + window.setResearcherModel(nullptr); + guiref = nullptr; } // Shutdown the core and its threads, but don't exit Bitcoin-Qt here LogPrintf("Main calling Shutdown..."); - Shutdown(NULL); + Shutdown(nullptr); } } @@ -696,7 +696,7 @@ int StartGridcoinQt(int argc, char *argv[], QApplication& app, OptionsModel& opt } catch (...) { - handleRunawayException(NULL); + handleRunawayException(nullptr); } // delete thread handler diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index 9710d122b6..2f83271992 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -10,7 +10,7 @@ class BitcoinAddressValidator : public QValidator { Q_OBJECT public: - explicit BitcoinAddressValidator(QObject *parent = 0); + explicit BitcoinAddressValidator(QObject* parent = nullptr); State validate(QString &input, int &pos) const; diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 54f10e23b9..2f62da5b04 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -14,8 +14,7 @@ #include #include -BitcoinAmountField::BitcoinAmountField(QWidget *parent): - QWidget(parent), amount(0), currentUnit(-1), valid(true) +BitcoinAmountField::BitcoinAmountField(QWidget* parent) : QWidget(parent), amount(nullptr), currentUnit(-1), valid(true) { amount = new QDoubleSpinBox(this); amount->setLocale(QLocale::c()); @@ -65,7 +64,7 @@ bool BitcoinAmountField::validate() bool valid = true; if (amount->value() == 0.0) valid = false; - if (valid && !BitcoinUnits::parse(currentUnit, text(), 0)) + if (valid && !BitcoinUnits::parse(currentUnit, text(), nullptr)) valid = false; setValid(valid); diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 165e7c82b6..bc735012c9 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -15,7 +15,7 @@ class BitcoinAmountField: public QWidget Q_OBJECT Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true) public: - explicit BitcoinAmountField(QWidget *parent = 0); + explicit BitcoinAmountField(QWidget* parent = nullptr); qint64 value(bool *valid=0) const; void setValue(qint64 value); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1c64888fc0..bed6b0b53d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -94,18 +94,17 @@ extern CWallet* pwalletMain; extern std::string FromQString(QString qs); extern CCriticalSection cs_ConvergedScraperStatsCache; -BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent), - clientModel(0), - walletModel(0), - encryptWalletAction(0), - changePassphraseAction(0), - unlockWalletAction(0), - lockWalletAction(0), - trayIcon(0), - notificator(0), - rpcConsole(0), - nWeight(0) +BitcoinGUI::BitcoinGUI(QWidget* parent) : QMainWindow(parent), + clientModel(nullptr), + walletModel(nullptr), + encryptWalletAction(nullptr), + changePassphraseAction(nullptr), + unlockWalletAction(nullptr), + lockWalletAction(nullptr), + trayIcon(nullptr), + notificator(nullptr), + rpcConsole(nullptr), + nWeight(0) { QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { @@ -1272,7 +1271,7 @@ void BitcoinGUI::gotoOverviewPage() centralWidget->setCurrentWidget(overviewPage); exportAction->setEnabled(false); - disconnect(exportAction, SIGNAL(triggered()), 0, 0); + disconnect(exportAction, SIGNAL(triggered()), nullptr, nullptr); } void BitcoinGUI::gotoHistoryPage() @@ -1281,7 +1280,7 @@ void BitcoinGUI::gotoHistoryPage() centralWidget->setCurrentWidget(transactionView); exportAction->setEnabled(true); - disconnect(exportAction, SIGNAL(triggered()), 0, 0); + disconnect(exportAction, SIGNAL(triggered()), nullptr, nullptr); connect(exportAction, SIGNAL(triggered()), transactionView, SLOT(exportClicked())); } @@ -1291,7 +1290,7 @@ void BitcoinGUI::gotoAddressBookPage() centralWidget->setCurrentWidget(addressBookPage); exportAction->setEnabled(true); - disconnect(exportAction, SIGNAL(triggered()), 0, 0); + disconnect(exportAction, SIGNAL(triggered()), nullptr, nullptr); connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked())); } @@ -1301,7 +1300,7 @@ void BitcoinGUI::gotoReceiveCoinsPage() centralWidget->setCurrentWidget(receiveCoinsPage); exportAction->setEnabled(true); - disconnect(exportAction, SIGNAL(triggered()), 0, 0); + disconnect(exportAction, SIGNAL(triggered()), nullptr, nullptr); connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked())); } @@ -1311,7 +1310,7 @@ void BitcoinGUI::gotoSendCoinsPage() centralWidget->setCurrentWidget(sendCoinsPage); exportAction->setEnabled(false); - disconnect(exportAction, SIGNAL(triggered()), 0, 0); + disconnect(exportAction, SIGNAL(triggered()), nullptr, nullptr); } void BitcoinGUI::gotoVotingPage() @@ -1320,7 +1319,7 @@ void BitcoinGUI::gotoVotingPage() centralWidget->setCurrentWidget(votingPage); exportAction->setEnabled(false); - disconnect(exportAction, SIGNAL(triggered()), 0, 0); + disconnect(exportAction, SIGNAL(triggered()), nullptr, nullptr); } void BitcoinGUI::gotoSignMessageTab(QString addr) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 3f6d7bddf7..63e1b73e47 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -47,7 +47,7 @@ class BitcoinGUI : public QMainWindow { Q_OBJECT public: - explicit BitcoinGUI(QWidget *parent = 0); + explicit BitcoinGUI(QWidget* parent = nullptr); ~BitcoinGUI(); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 0b51642b7e..9e81a56b12 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -21,9 +21,8 @@ static const int64_t nClientStartupTime = GetTime(); extern ConvergedScraperStats ConvergedScraperStatsCache; -ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : - QObject(parent), optionsModel(optionsModel), peerTableModel(nullptr), - banTableModel(nullptr), cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0) +ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) : QObject(parent), optionsModel(optionsModel), peerTableModel(nullptr), + banTableModel(nullptr), cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(nullptr) { numBlocksAtStartup = -1; peerTableModel = new PeerTableModel(this); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index d088e83efd..35ce2cb6c5 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -22,7 +22,7 @@ class ClientModel : public QObject { Q_OBJECT public: - explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); + explicit ClientModel(OptionsModel* optionsModel, QObject* parent = nullptr); ~ClientModel(); OptionsModel *getOptionsModel(); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 9f3ae29d47..b15a313857 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -26,13 +26,12 @@ using namespace std; -CoinControlDialog::CoinControlDialog(QWidget *parent, CCoinControl *coinControl, QList *payAmounts) : - QDialog(parent), - m_inputSelectionLimit(GetMaxInputsForConsolidationTxn()), - ui(new Ui::CoinControlDialog), - coinControl(coinControl), - payAmounts(payAmounts), - model(0) +CoinControlDialog::CoinControlDialog(QWidget* parent, CCoinControl* coinControl, QList* payAmounts) : QDialog(parent), + m_inputSelectionLimit(GetMaxInputsForConsolidationTxn()), + ui(new Ui::CoinControlDialog), + coinControl(coinControl), + payAmounts(payAmounts), + model(nullptr) { assert(coinControl != nullptr && payAmounts != nullptr); diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 820261b822..36c27a0fc5 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -24,9 +24,9 @@ class CoinControlDialog : public QDialog Q_OBJECT public: - explicit CoinControlDialog(QWidget *parent = 0, - CCoinControl *coinControl = nullptr, - QList *payAmounts = nullptr); + explicit CoinControlDialog(QWidget* parent = nullptr, + CCoinControl* coinControl = nullptr, + QList* payAmounts = nullptr); ~CoinControlDialog(); void setModel(WalletModel *model); diff --git a/src/qt/coincontroltreewidget.h b/src/qt/coincontroltreewidget.h index 654bd35c3f..a1118095aa 100644 --- a/src/qt/coincontroltreewidget.h +++ b/src/qt/coincontroltreewidget.h @@ -8,8 +8,8 @@ class CoinControlTreeWidget : public QTreeWidget { Q_OBJECT public: - explicit CoinControlTreeWidget(QWidget *parent = 0); - + explicit CoinControlTreeWidget(QWidget* parent = nullptr); + protected: virtual void keyPressEvent(QKeyEvent *event); }; diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index 8a50bbab3f..c0acd474ea 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -4,9 +4,8 @@ #include #include -CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) : - QObject(parent), - filename(filename), model(0) +CSVModelWriter::CSVModelWriter(const QString& filename, QObject* parent) : QObject(parent), + filename(filename), model(nullptr) { } diff --git a/src/qt/csvmodelwriter.h b/src/qt/csvmodelwriter.h index 6c9dcbaf3b..ae88618e8d 100644 --- a/src/qt/csvmodelwriter.h +++ b/src/qt/csvmodelwriter.h @@ -15,7 +15,7 @@ class CSVModelWriter : public QObject { Q_OBJECT public: - explicit CSVModelWriter(const QString &filename, QObject *parent = 0); + explicit CSVModelWriter(const QString& filename, QObject* parent = nullptr); void setModel(const QAbstractItemModel *model); void addColumn(const QString &title, int column, int role=Qt::EditRole); diff --git a/src/qt/diagnosticsdialog.h b/src/qt/diagnosticsdialog.h index 6641dee198..0a7e16d5e3 100644 --- a/src/qt/diagnosticsdialog.h +++ b/src/qt/diagnosticsdialog.h @@ -25,7 +25,7 @@ class DiagnosticsDialog : public QDialog Q_OBJECT public: - explicit DiagnosticsDialog(QWidget *parent = 0, ResearcherModel* researcher_model = nullptr); + explicit DiagnosticsDialog(QWidget* parent = nullptr, ResearcherModel* researcher_model = nullptr); ~DiagnosticsDialog(); enum DiagnosticResult diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index d8c7109f59..4d655d2e5a 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -6,9 +6,8 @@ #include #include -EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) : - QDialog(parent), - ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0) +EditAddressDialog::EditAddressDialog(Mode mode, QWidget* parent) : QDialog(parent), + ui(new Ui::EditAddressDialog), mapper(nullptr), mode(mode), model(nullptr) { ui->setupUi(this); diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 0e4183bd52..50be2f59d7 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -26,7 +26,7 @@ class EditAddressDialog : public QDialog EditSendingAddress }; - explicit EditAddressDialog(Mode mode, QWidget *parent = 0); + explicit EditAddressDialog(Mode mode, QWidget* parent = nullptr); ~EditAddressDialog(); void setModel(AddressTableModel *model); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp old mode 100755 new mode 100644 index 4f9f9c176e..42000bfb2d --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -520,19 +520,19 @@ bool SetStartOnSystemStartup(bool fAutoStart, bool fStartMin) if (fAutoStart) { - CoInitialize(NULL); + CoInitialize(nullptr); // Get a pointer to the IShellLink interface. - IShellLinkW* psl = NULL; - HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, - CLSCTX_INPROC_SERVER, IID_IShellLinkW, - reinterpret_cast(&psl)); + IShellLinkW* psl = nullptr; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, + CLSCTX_INPROC_SERVER, IID_IShellLinkW, + reinterpret_cast(&psl)); if (SUCCEEDED(hres)) { // Get the current executable path WCHAR pszExePath[MAX_PATH]; - GetModuleFileNameW(NULL, pszExePath, sizeof(pszExePath)); + GetModuleFileNameW(nullptr, pszExePath, sizeof(pszExePath)); std::wstring autostartup_arguments; std::wstring_convert> converter; @@ -566,7 +566,7 @@ bool SetStartOnSystemStartup(bool fAutoStart, bool fStartMin) // Query IShellLink for the IPersistFile interface for // saving the shortcut in persistent storage. - IPersistFile* ppf = NULL; + IPersistFile* ppf = nullptr; hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast(&ppf)); if (SUCCEEDED(hres)) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 25b2520c53..38c7546042 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -109,7 +109,7 @@ namespace GUIUtil Q_OBJECT public: - explicit ToolTipToRichTextFilter(int size_threshold, QObject *parent = 0); + explicit ToolTipToRichTextFilter(int size_threshold, QObject* parent = nullptr); protected: bool eventFilter(QObject *obj, QEvent *evt); @@ -126,7 +126,7 @@ namespace GUIUtil Q_OBJECT public: - explicit WindowContextHelpButtonHintFilter(QObject *parent = 0); + explicit WindowContextHelpButtonHintFilter(QObject* parent = nullptr); protected: bool eventFilter(QObject *obj, QEvent *evt); @@ -142,7 +142,7 @@ namespace GUIUtil Q_OBJECT public: - HelpMessageBox(QWidget *parent = 0); + HelpMessageBox(QWidget* parent = nullptr); /** Show message box or print help message to standard output, based on operating system. */ void showOrPrint(); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 1c21b882da..209cf1deba 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -192,7 +192,7 @@ bool Intro::showIfNeeded(bool& did_show_intro) } /* If current default data directory does not exist, let the user choose one */ - Intro intro(0, Params().AssumedBlockchainSize()); + Intro intro(nullptr, Params().AssumedBlockchainSize()); intro.setDataDirectory(dataDir); intro.setWindowIcon(QIcon(":/images/gridcoin")); did_show_intro = true; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index f66e4b8aef..d2ddcfc8ef 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -14,14 +14,13 @@ #include #include -OptionsDialog::OptionsDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::OptionsDialog), - model(0), - mapper(0), - fRestartWarningDisplayed_Proxy(false), - fRestartWarningDisplayed_Lang(false), - fProxyIpValid(true) +OptionsDialog::OptionsDialog(QWidget* parent) : QDialog(parent), + ui(new Ui::OptionsDialog), + model(nullptr), + mapper(nullptr), + fRestartWarningDisplayed_Proxy(false), + fRestartWarningDisplayed_Lang(false), + fProxyIpValid(true) { ui->setupUi(this); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 4509795bdd..0beda2c179 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -16,7 +16,7 @@ class OptionsDialog : public QDialog Q_OBJECT public: - explicit OptionsDialog(QWidget *parent = 0); + explicit OptionsDialog(QWidget* parent = nullptr); ~OptionsDialog(); void setModel(OptionsModel *model); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 6d77b0a794..0a6b317fd0 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -15,7 +15,7 @@ class OptionsModel : public QAbstractListModel Q_OBJECT public: - explicit OptionsModel(QObject *parent = 0); + explicit OptionsModel(QObject* parent = nullptr); enum OptionID { StartAtStartup, // bool diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 01c67db5d1..47ef90545a 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -22,7 +22,7 @@ class OverviewPage : public QWidget Q_OBJECT public: - explicit OverviewPage(QWidget *parent = 0); + explicit OverviewPage(QWidget* parent = nullptr); ~OverviewPage(); void setResearcherModel(ResearcherModel *model); diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp index 722e5a88e7..00de650141 100644 --- a/src/qt/qrcodedialog.cpp +++ b/src/qt/qrcodedialog.cpp @@ -11,11 +11,10 @@ #include -QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) : - QDialog(parent), - ui(new Ui::QRCodeDialog), - model(0), - address(addr) +QRCodeDialog::QRCodeDialog(const QString& addr, const QString& label, bool enableReq, QWidget* parent) : QDialog(parent), + ui(new Ui::QRCodeDialog), + model(nullptr), + address(addr) { ui->setupUi(this); diff --git a/src/qt/qrcodedialog.h b/src/qt/qrcodedialog.h index b7d11b8184..0b79df1812 100644 --- a/src/qt/qrcodedialog.h +++ b/src/qt/qrcodedialog.h @@ -14,7 +14,7 @@ class QRCodeDialog : public QDialog Q_OBJECT public: - explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0); + explicit QRCodeDialog(const QString& addr, const QString& label, bool enableReq, QWidget* parent = nullptr); ~QRCodeDialog(); void setModel(OptionsModel *model); diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp index 140d93d0e0..b1fdea4be8 100644 --- a/src/qt/qtipcserver.cpp +++ b/src/qt/qtipcserver.cpp @@ -83,7 +83,7 @@ static void ipcThread(void* pArg) catch (std::exception& e) { PrintExceptionContinue(&e, "ipcThread()"); } catch (...) { - PrintExceptionContinue(NULL, "ipcThread()"); + PrintExceptionContinue(nullptr, "ipcThread()"); } LogPrintf("ipcThread exited"); } @@ -118,7 +118,7 @@ static void ipcThread2(void* pArg) void ipcInit(int argc, char *argv[]) { - message_queue* mq = NULL; + message_queue* mq = nullptr; char buffer[MAX_URI_LENGTH + 1] = ""; size_t nSize = 0; unsigned int nPriority = 0; diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index 66e26be9a3..793e56e247 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -10,7 +10,7 @@ class QValidatedLineEdit : public QLineEdit { Q_OBJECT public: - explicit QValidatedLineEdit(QWidget *parent = 0); + explicit QValidatedLineEdit(QWidget* parent = nullptr); void clear(); protected: diff --git a/src/qt/qvaluecombobox.h b/src/qt/qvaluecombobox.h index 1a47bb6565..69f3341328 100644 --- a/src/qt/qvaluecombobox.h +++ b/src/qt/qvaluecombobox.h @@ -10,7 +10,7 @@ class QValueComboBox : public QComboBox Q_OBJECT Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged USER true) public: - explicit QValueComboBox(QWidget *parent = 0); + explicit QValueComboBox(QWidget* parent = nullptr); QVariant value() const; void setValue(const QVariant &value); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index e41fe22c83..6df3cf7e16 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -44,8 +44,7 @@ const struct { {"cmd-reply", ":/icons/tx_output"}, {"cmd-error", ":/icons/tx_output"}, {"misc", ":/icons/tx_inout"}, - {NULL, NULL} -}; + {nullptr, nullptr}}; /* Object for executing console RPC commands in a separate thread. */ diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 9033ca441c..db2f9ccb2e 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -27,7 +27,7 @@ class RPCConsole: public QDialog Q_OBJECT public: - explicit RPCConsole(QWidget *parent = 0); + explicit RPCConsole(QWidget* parent = nullptr); ~RPCConsole(); void setClientModel(ClientModel *model); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 60862736ec..3b90368ae5 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -24,12 +24,11 @@ #include #include -SendCoinsDialog::SendCoinsDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SendCoinsDialog), - coinControl(new CCoinControl), - payAmounts(new QList), - model(0) +SendCoinsDialog::SendCoinsDialog(QWidget* parent) : QDialog(parent), + ui(new Ui::SendCoinsDialog), + coinControl(new CCoinControl), + payAmounts(new QList), + model(nullptr) { ui->setupUi(this); @@ -298,7 +297,7 @@ void SendCoinsDialog::updateRemoveEnabled() entry->setMessageEnabled(i == 0); } } - setupTabChain(0); + setupTabChain(nullptr); coinControlUpdateLabels(); } @@ -328,7 +327,7 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) if(!fNewRecipientAllowed) return; - SendCoinsEntry *entry = 0; + SendCoinsEntry* entry = nullptr; // Replace the first entry if it is still unused if(ui->entries->count() == 1) { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 33903cc5f9..2ed6eb707a 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -21,7 +21,7 @@ class SendCoinsDialog : public QDialog Q_OBJECT public: - explicit SendCoinsDialog(QWidget *parent = 0); + explicit SendCoinsDialog(QWidget* parent = nullptr); ~SendCoinsDialog(); void setModel(WalletModel *model); diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index ddc5fa1198..8c745478c2 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -10,10 +10,9 @@ #include #include -SendCoinsEntry::SendCoinsEntry(QWidget *parent) : - QFrame(parent), - ui(new Ui::SendCoinsEntry), - model(0) +SendCoinsEntry::SendCoinsEntry(QWidget* parent) : QFrame(parent), + ui(new Ui::SendCoinsEntry), + model(nullptr) { ui->setupUi(this); diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index c1592352ef..34713bbdac 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -15,7 +15,7 @@ class SendCoinsEntry : public QFrame Q_OBJECT public: - explicit SendCoinsEntry(QWidget *parent = 0); + explicit SendCoinsEntry(QWidget* parent = nullptr); ~SendCoinsEntry(); void setModel(WalletModel *model); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index fb62ed47e7..27521d970f 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -16,10 +16,9 @@ #include -SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SignVerifyMessageDialog), - model(0) +SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget* parent) : QDialog(parent), + ui(new Ui::SignVerifyMessageDialog), + model(nullptr) { ui->setupUi(this); diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index 5569c8bf33..dd6b202d2f 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -16,7 +16,7 @@ class SignVerifyMessageDialog : public QDialog Q_OBJECT public: - explicit SignVerifyMessageDialog(QWidget *parent = 0); + explicit SignVerifyMessageDialog(QWidget* parent = nullptr); ~SignVerifyMessageDialog(); void setModel(WalletModel *model); diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 7eda0af46b..1798419e92 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -14,16 +14,15 @@ #define XMARGIN 10 #define YMARGIN 10 -TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : - QWidget(parent), - timer(0), - fMax(0.0f), - nMins(0), - vSamplesIn(), - vSamplesOut(), - nLastBytesIn(0), - nLastBytesOut(0), - clientModel(0) +TrafficGraphWidget::TrafficGraphWidget(QWidget* parent) : QWidget(parent), + timer(nullptr), + fMax(0.0f), + nMins(0), + vSamplesIn(), + vSamplesOut(), + nLastBytesIn(0), + nLastBytesOut(0), + clientModel(nullptr) { timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(updateRates())); diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h index e2a6e60f6e..490ffe585a 100644 --- a/src/qt/trafficgraphwidget.h +++ b/src/qt/trafficgraphwidget.h @@ -16,7 +16,7 @@ class TrafficGraphWidget : public QWidget Q_OBJECT public: - explicit TrafficGraphWidget(QWidget *parent = 0); + explicit TrafficGraphWidget(QWidget* parent = nullptr); void setClientModel(ClientModel *model); int getGraphRangeMins() const; diff --git a/src/qt/transactiondescdialog.h b/src/qt/transactiondescdialog.h index e86fb58a64..eea4a6e10f 100644 --- a/src/qt/transactiondescdialog.h +++ b/src/qt/transactiondescdialog.h @@ -16,7 +16,7 @@ class TransactionDescDialog : public QDialog Q_OBJECT public: - explicit TransactionDescDialog(const QModelIndex &idx, QWidget *parent = 0); + explicit TransactionDescDialog(const QModelIndex& idx, QWidget* parent = nullptr); ~TransactionDescDialog(); private: diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 429ce1407c..6babb9b782 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -9,7 +9,7 @@ class TransactionFilterProxy : public QSortFilterProxyModel { Q_OBJECT public: - explicit TransactionFilterProxy(QObject *parent = 0); + explicit TransactionFilterProxy(QObject* parent = nullptr); /** Earliest date that can be represented (far in the past) */ static const QDateTime MIN_DATE; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 0940bee350..8d4a1df13b 100755 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -353,7 +353,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // Determine transaction status // Find the block the tx is in - CBlockIndex* pindex = NULL; + CBlockIndex* pindex = nullptr; BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock); if (mi != mapBlockIndex.end()) pindex = mi->second; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index f620afd263..f9688eb21e 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -235,7 +235,7 @@ class TransactionTablePriv } else { - return 0; + return nullptr; } } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 2c7d07dee4..e25f767b9a 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -15,7 +15,7 @@ class TransactionTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit TransactionTableModel(CWallet* wallet, WalletModel *parent = 0); + explicit TransactionTableModel(CWallet* wallet, WalletModel* parent = nullptr); ~TransactionTableModel(); enum ColumnIndex { diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 7f1bead3e4..d4dee755e3 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -23,7 +23,7 @@ class TransactionView : public QFrame { Q_OBJECT public: - explicit TransactionView(QWidget *parent = 0); + explicit TransactionView(QWidget* parent = nullptr); void setModel(WalletModel *model); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 55194749e7..01d55297b9 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -15,13 +15,12 @@ void qtInsertConfirm(double dAmt, std::string sFrom, std::string sTo, std::string txid); -WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), - transactionTableModel(0), - cachedBalance(0), cachedStake(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), - cachedNumTransactions(0), - cachedEncryptionStatus(Unencrypted), - cachedNumBlocks(0) +WalletModel::WalletModel(CWallet* wallet, OptionsModel* optionsModel, QObject* parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(nullptr), + transactionTableModel(nullptr), + cachedBalance(0), cachedStake(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), + cachedNumTransactions(0), + cachedEncryptionStatus(Unencrypted), + cachedNumBlocks(0) { addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); @@ -497,7 +496,7 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect void WalletModel::listCoins(std::map >& mapCoins) const { std::vector vCoins; - wallet->AvailableCoins(vCoins,true,NULL,false); + wallet->AvailableCoins(vCoins, true, nullptr, false); LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet std::vector vLockedCoins; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 2c2bfaba46..248f8386b0 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -38,7 +38,7 @@ class WalletModel : public QObject Q_OBJECT public: - explicit WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); + explicit WalletModel(CWallet* wallet, OptionsModel* optionsModel, QObject* parent = nullptr); ~WalletModel(); enum StatusCode // Returned by sendCoins @@ -88,7 +88,7 @@ class WalletModel : public QObject }; // Send coins to a list of recipients - SendCoinsReturn sendCoins(const QList &recipients, const CCoinControl *coinControl=NULL); + SendCoinsReturn sendCoins(const QList& recipients, const CCoinControl* coinControl = nullptr); // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 40f49bfbdc..c4c2a2d193 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -345,7 +345,7 @@ UniValue showblock(const UniValue& params, bool fHelp) CBlockIndex* pblockindex = RPCBlockFinder.FindByHeight(nHeight); - if (pblockindex==NULL) + if (pblockindex == nullptr) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); CBlock block; block.ReadFromDisk(pblockindex); @@ -2056,8 +2056,7 @@ UniValue getcheckpoint(const UniValue& params, bool fHelp) LOCK(cs_main); const CBlockIndex* pindexCheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if(pindexCheckpoint != NULL) - { + if (pindexCheckpoint != nullptr) { result.pushKV("synccheckpoint", pindexCheckpoint->GetBlockHash().ToString().c_str()); result.pushKV("height", pindexCheckpoint->nHeight); result.pushKV("timestamp", DateTimeStrFormat(pindexCheckpoint->GetBlockTime()).c_str()); diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 08d0ea6c2d..9b2eee4d2f 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -42,7 +42,7 @@ class MockBlockIndex : CDiskBlockIndex static CBlockIndex* InsertBlockIndex(const uint256& hash) { if (hash.IsNull()) - return NULL; + return nullptr; // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 82958741f1..1127397f77 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -343,7 +343,7 @@ int CommandLineRPC(int argc, char *argv[]) } catch (...) { - PrintException(NULL, "CommandLineRPC()"); + PrintException(nullptr, "CommandLineRPC()"); } if (strPrint != "") diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 7bad395f58..6ad6b01a41 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -53,7 +53,7 @@ std::string rfc1123Time() time_t now; time(&now); struct tm* now_gmt = gmtime(&now); - std::string locale(setlocale(LC_TIME, NULL)); + std::string locale(setlocale(LC_TIME, nullptr)); setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); setlocale(LC_TIME, locale.c_str()); @@ -158,7 +158,7 @@ bool ReadHTTPRequestLine(std::basic_istream& stream, int &proto, proto = 0; const char *ver = strstr(strProto.c_str(), "HTTP/1."); - if (ver != NULL) + if (ver != nullptr) proto = atoi(ver+7); return true; @@ -174,7 +174,7 @@ int ReadHTTPStatus(std::basic_istream& stream, int &proto) return HTTP_INTERNAL_SERVER_ERROR; proto = 0; const char *ver = strstr(str.c_str(), "HTTP/1."); - if (ver != NULL) + if (ver != nullptr) proto = atoi(ver+7); return atoi(vWords[1].c_str()); } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index e659e9adf8..a07293218b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -36,7 +36,7 @@ std::vector> GetTxStakeBoincHashInfo(const C std::vector> res; // Fetch BlockIndex for tx block - CBlockIndex* pindex = NULL; + CBlockIndex* pindex = nullptr; CBlock block; { BlockMap::iterator mi = mapBlockIndex.find(mtx.hashBlock); @@ -442,7 +442,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) vector vecOutputs; - pwalletMain->AvailableCoins(vecOutputs, false, NULL, false); + pwalletMain->AvailableCoins(vecOutputs, false, nullptr, false); LOCK(pwalletMain->cs_wallet); @@ -576,7 +576,7 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Get the current UTXO's. - pwalletMain->AvailableCoins(vecInputs, false, NULL, false); + pwalletMain->AvailableCoins(vecInputs, false, nullptr, false); // Filter outputs in the wallet that are the candidate inputs by matching address and insert into sorted multimap. for (auto const& out : vecInputs) @@ -1761,10 +1761,10 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) else { // push to local node - if (!AcceptToMemoryPool(mempool, tx, NULL)) + if (!AcceptToMemoryPool(mempool, tx, nullptr)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); - SyncWithWallets(tx, NULL, true); + SyncWithWallets(tx, nullptr, true); } RelayTransaction(tx, hashTx); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 2963d0fe79..0155e7299a 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -36,9 +36,9 @@ void ThreadRPCServer2(void* parg); static std::string strRPCUserColonPass; // These are created by StartRPCThreads, destroyed in StopRPCThreads -static ioContext* rpc_io_service = NULL; -static ssl::context* rpc_ssl_context = NULL; -static boost::thread_group* rpc_worker_group = NULL; +static ioContext* rpc_io_service = nullptr; +static ssl::context* rpc_ssl_context = nullptr; +static boost::thread_group* rpc_worker_group = nullptr; const UniValue emptyobj(UniValue::VOBJ); @@ -466,7 +466,7 @@ const CRPCCommand *CRPCTable::operator[](string name) const { map::const_iterator it = mapCommands.find(name); if (it == mapCommands.end()) - return NULL; + return nullptr; return it->second; } @@ -606,7 +606,7 @@ void StartRPCThreads() const bool fUseSSL = gArgs.GetBoolArg("-rpcssl"); - assert(rpc_io_service == NULL); + assert(rpc_io_service == nullptr); rpc_io_service = new ioContext(); rpc_ssl_context = new ssl::context(ssl::context::sslv23); @@ -703,15 +703,15 @@ void StopRPCThreads() } rpc_io_service->stop(); - if (rpc_worker_group != NULL) + if (rpc_worker_group != nullptr) rpc_worker_group->join_all(); delete rpc_worker_group; - rpc_worker_group = NULL; + rpc_worker_group = nullptr; delete rpc_ssl_context; - rpc_ssl_context = NULL; + rpc_ssl_context = nullptr; delete rpc_io_service; - rpc_io_service = NULL; + rpc_io_service = nullptr; } class JSONRequest @@ -927,18 +927,18 @@ int main(int argc, char *argv[]) #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)); #endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); + setbuf(stdin, nullptr); + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); try { if (argc >= 2 && string(argv[1]) == "-server") { LogPrintf("server ready"); - ThreadRPCServer(NULL); + ThreadRPCServer(nullptr); } else { @@ -948,7 +948,7 @@ int main(int argc, char *argv[]) catch (std::exception& e) { PrintException(&e, "main()"); } catch (...) { - PrintException(NULL, "main()"); + PrintException(nullptr, "main()"); } return 0; } diff --git a/src/script.cpp b/src/script.cpp index 5d63cfa3a3..9dd4720f68 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -102,7 +102,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_MULTISIG: return "multisig"; case TX_NULL_DATA: return "nulldata"; } - return NULL; + return nullptr; } @@ -886,7 +886,7 @@ bool EvalScript(vector >& stack, const CScript& script, co break; case OP_DIV: - if (!BN_div(&bn, NULL, &bn1, &bn2, pctx)) + if (!BN_div(&bn, nullptr, &bn1, &bn2, pctx)) return false; break; diff --git a/src/script.h b/src/script.h index ceb22cecae..c566521a21 100644 --- a/src/script.h +++ b/src/script.h @@ -421,7 +421,7 @@ class CScript : public CScriptBase bool GetOp(iterator& pc, opcodetype& opcodeRet) { const_iterator pc2 = pc; - bool fRet = GetOp2(pc2, opcodeRet, NULL); + bool fRet = GetOp2(pc2, opcodeRet, nullptr); pc = begin() + (pc2 - begin()); return fRet; } @@ -433,7 +433,7 @@ class CScript : public CScriptBase bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const { - return GetOp2(pc, opcodeRet, NULL); + return GetOp2(pc, opcodeRet, nullptr); } bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector* pvchRet) const diff --git a/src/sync.h b/src/sync.h index a92ed4f631..9b0efa017d 100644 --- a/src/sync.h +++ b/src/sync.h @@ -396,7 +396,7 @@ class CSemaphoreGrant fHaveGrant = false; } - CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} + CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {} explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { diff --git a/src/test/gridcoin/superblock_tests.cpp b/src/test/gridcoin/superblock_tests.cpp index 9aeb755f3f..7438c6e481 100644 --- a/src/test/gridcoin/superblock_tests.cpp +++ b/src/test/gridcoin/superblock_tests.cpp @@ -110,7 +110,7 @@ struct Legacy std::copy_n(binary_cpid.begin(), researcher.cpid.size(), researcher.cpid.begin()); // Ensure we do not blow out the binary space (technically we can handle 0-65535) - double magnitude_d = strtod(ExtractValue(entry, ",", 1).c_str(), NULL); + double magnitude_d = strtod(ExtractValue(entry, ",", 1).c_str(), nullptr); // Changed to 65535 for the new NN. This will still be able to be successfully unpacked by any node. magnitude_d = std::clamp(magnitude_d, 0.0, 65535.0); researcher.magnitude = htobe16(roundint(magnitude_d)); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index cd832e983b..64e9ec7801 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -12,7 +12,7 @@ using namespace std; BOOST_AUTO_TEST_SUITE(rpc_tests) static UniValue -createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) +createArgs(int nRequired, const char* address1 = nullptr, const char* address2 = nullptr) { UniValue result(UniValue::VARR); result.push_back(nRequired); diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp index 60922ef797..89e9ca8a9b 100644 --- a/src/txdb-leveldb.cpp +++ b/src/txdb-leveldb.cpp @@ -69,7 +69,7 @@ void init_blockindex(leveldb::Options& options, bool fRemoveOld = false) { CTxDB::CTxDB(const char* pszMode) { assert(pszMode); - activeBatch = NULL; + activeBatch = nullptr; fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); if (txdb) { @@ -97,9 +97,9 @@ CTxDB::CTxDB(const char* pszMode) // Leveldb instance destruction delete txdb; - txdb = pdb = NULL; + txdb = pdb = nullptr; delete activeBatch; - activeBatch = NULL; + activeBatch = nullptr; init_blockindex(options, true); // Remove directory and create new database pdb = txdb; @@ -124,13 +124,13 @@ CTxDB::CTxDB(const char* pszMode) void CTxDB::Close() { delete txdb; - txdb = pdb = NULL; + txdb = pdb = nullptr; delete options.filter_policy; - options.filter_policy = NULL; + options.filter_policy = nullptr; delete options.block_cache; - options.block_cache = NULL; + options.block_cache = nullptr; delete activeBatch; - activeBatch = NULL; + activeBatch = nullptr; } bool CTxDB::TxnBegin() @@ -145,7 +145,7 @@ bool CTxDB::TxnCommit() assert(activeBatch); leveldb::Status status = pdb->Write(leveldb::WriteOptions(), activeBatch); delete activeBatch; - activeBatch = NULL; + activeBatch = nullptr; if (!status.ok()) { LogPrintf("LevelDB batch commit failure: %s", status.ToString()); return false; @@ -286,7 +286,7 @@ bool CTxDB::WriteGenericData(const std::string& strKey,const std::string& strDat static CBlockIndex *InsertBlockIndex(const uint256& hash) { if (hash.IsNull()) - return NULL; + return nullptr; // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); @@ -398,7 +398,7 @@ bool CTxDB::LoadBlockIndex() nBlockCount++; // Watch for genesis block - if (pindexGenesisBlock == NULL && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + if (pindexGenesisBlock == nullptr && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) pindexGenesisBlock = pindexNew; if(fQtActive) @@ -431,7 +431,7 @@ bool CTxDB::LoadBlockIndex() // Load hashBestChain pointer to end of best chain if (!ReadHashBestChain(hashBestChain)) { - if (pindexGenesisBlock == NULL) + if (pindexGenesisBlock == nullptr) return true; return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); } @@ -451,7 +451,7 @@ bool CTxDB::LoadBlockIndex() int nCheckDepth = gArgs.GetArg( "-checkblocks", 1000); LogPrintf("Verifying last %i blocks at level %i", nCheckDepth, nCheckLevel); - CBlockIndex* pindexFork = NULL; + CBlockIndex* pindexFork = nullptr; map, CBlockIndex*> mapBlockPos; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { diff --git a/src/txdb-leveldb.h b/src/txdb-leveldb.h index d11bb32718..188c72eee5 100644 --- a/src/txdb-leveldb.h +++ b/src/txdb-leveldb.h @@ -42,7 +42,7 @@ class CTxDB leveldb::DB *pdb; // Points to the global instance. // A batch stores up writes and deletes for atomic application. When this - // field is non-NULL, writes/deletes go there instead of directly to disk. + // field is non-nullptr, writes/deletes go there instead of directly to disk. leveldb::WriteBatch *activeBatch; leveldb::Options options; bool fReadOnly; @@ -166,7 +166,7 @@ class CTxDB bool TxnAbort() { delete activeBatch; - activeBatch = NULL; + activeBatch = nullptr; return true; } diff --git a/src/util.cpp b/src/util.cpp index 9987f8743a..5de3d0ae69 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -102,7 +102,7 @@ class CInit // Securely erase the memory used by the PRNG RAND_cleanup(); // Shutdown OpenSSL library multithreading support - CRYPTO_set_locking_callback(NULL); + CRYPTO_set_locking_callback(nullptr); for (int i = 0; i < CRYPTO_num_locks(); i++) delete ppmutexOpenSSL[i]; OPENSSL_free(ppmutexOpenSSL); @@ -134,7 +134,7 @@ void RandAddSeedPerfmon() unsigned char pdata[250000]; memset(pdata, 0, sizeof(pdata)); unsigned long nSize = sizeof(pdata); - long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); + long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, pdata, &nSize); RegCloseKey(HKEY_PERFORMANCE_DATA); if (ret == ERROR_SUCCESS) { diff --git a/src/util.h b/src/util.h index 75f775f9e2..5f0a780fd0 100644 --- a/src/util.h +++ b/src/util.h @@ -191,7 +191,7 @@ inline int64_t GetPerformanceCounter() QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); #else timeval t; - gettimeofday(&t, NULL); + gettimeofday(&t, nullptr); nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec; #endif return nCounter; diff --git a/src/util/system.cpp b/src/util/system.cpp index bbacfb38f0..6af6aca2cb 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -672,7 +672,7 @@ static std::string FormatException(std::exception* pex, const char* pszThread) { #ifdef WIN32 char pszModule[MAX_PATH] = ""; - GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); + GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule)); #else const char* pszModule = "gridcoin"; #endif diff --git a/src/util/time.cpp b/src/util/time.cpp index 4664aa7f41..c761efce74 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -24,7 +24,7 @@ int64_t GetTime() { if (nMockTime) return nMockTime; - return time(NULL); + return time(nullptr); } int64_t GetMockTime() diff --git a/src/validation.h b/src/validation.h index 9e84d93bd5..c67565d8e4 100644 --- a/src/validation.h +++ b/src/validation.h @@ -17,7 +17,7 @@ class CBlockHeader; typedef std::map> MapPrevTx; -bool ReadTxFromDisk(CTransaction& tx, CDiskTxPos pos, FILE** pfileRet=NULL); +bool ReadTxFromDisk(CTransaction& tx, CDiskTxPos pos, FILE** pfileRet = nullptr); bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet); bool ReadTxFromDisk(CTransaction& tx, CTxDB& txdb, COutPoint prevout); bool ReadTxFromDisk(CTransaction& tx, COutPoint prevout); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 2eb6a387dc..860e75f40b 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -129,15 +129,15 @@ void CDBEnv::MakeMock() #ifdef DB_LOG_IN_MEMORY dbenv.log_set_config(DB_LOG_IN_MEMORY, 1); #endif - int ret = dbenv.open(NULL, - DB_CREATE | - DB_INIT_LOCK | - DB_INIT_LOG | - DB_INIT_MPOOL | - DB_INIT_TXN | - DB_THREAD | - DB_PRIVATE, - S_IRUSR | S_IWUSR); + int ret = dbenv.open(nullptr, + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_PRIVATE, + S_IRUSR | S_IWUSR); if (ret > 0) throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret)); @@ -151,10 +151,10 @@ CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDB assert(mapFileUseCount.count(strFile) == 0); Db db(&dbenv, 0); - int result = db.verify(strFile.c_str(), NULL, NULL, 0); + int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); if (result == 0) return VERIFY_OK; - else if (recoverFunc == NULL) + else if (recoverFunc == nullptr) return RECOVER_FAIL; // Try to recover: @@ -174,7 +174,7 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive, stringstream strDump; Db db(&dbenv, 0); - int result = db.verify(strFile.c_str(), NULL, &strDump, flags); + int result = db.verify(strFile.c_str(), nullptr, &strDump, flags); if (result == DB_VERIFY_BAD) { LogPrintf("Error: Salvage found errors, all data may not be recoverable."); @@ -233,8 +233,7 @@ void CDBEnv::lsn_reset(const std::string& strFile) dbenv.lsn_reset(strFile.c_str(),0); } -CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : - pdb(NULL), activeTxn(NULL) +CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr) { int ret; @@ -256,8 +255,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose strFile = strFilename; ++bitdb.mapFileUseCount[strFile]; pdb = bitdb.mapDb[strFile]; - if (pdb == NULL) - { + if (pdb == nullptr) { pdb = new Db(&bitdb.dbenv, 0); bool fMockDb = bitdb.IsMock(); @@ -269,17 +267,17 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", strFile)); } - ret = pdb->open(NULL, // Txn pointer - fMockDb ? NULL : strFile.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags + ret = pdb->open(nullptr, // Txn pointer + fMockDb ? nullptr : strFile.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags 0); if (ret != 0) { delete pdb; - pdb = NULL; + pdb = nullptr; --bitdb.mapFileUseCount[strFile]; strFile = ""; throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", strFile, ret)); @@ -317,8 +315,8 @@ void CDB::Close() return; if (activeTxn) activeTxn->abort(); - activeTxn = NULL; - pdb = NULL; + activeTxn = nullptr; + pdb = nullptr; if (fFlushOnClose) Flush(); @@ -333,13 +331,12 @@ void CDBEnv::CloseDb(const string& strFile) { { LOCK(cs_db); - if (mapDb[strFile] != NULL) - { + if (mapDb[strFile] != nullptr) { // Close the database handle Db* pdb = mapDb[strFile]; pdb->close(0); delete pdb; - mapDb[strFile] = NULL; + mapDb[strFile] = nullptr; } } } @@ -349,7 +346,7 @@ bool CDBEnv::RemoveDb(const string& strFile) this->CloseDb(strFile); LOCK(cs_db); - int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); + int rc = dbenv.dbremove(nullptr, strFile.c_str(), nullptr, DB_AUTO_COMMIT); return (rc == 0); } @@ -373,11 +370,11 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) CDB db(strFile.c_str(), "r"); Db* pdbCopy = new Db(&bitdb.dbenv, 0); - int ret = pdbCopy->open(NULL, // Txn pointer - strFileRes.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - DB_CREATE, // Flags + int ret = pdbCopy->open(nullptr, // Txn pointer + strFileRes.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags 0); if (ret > 0) { @@ -414,7 +411,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) } Dbt datKey(&ssKey[0], ssKey.size()); Dbt datValue(&ssValue[0], ssValue.size()); - int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE); if (ret2 > 0) fSuccess = false; } @@ -430,10 +427,10 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) if (fSuccess) { Db dbA(&bitdb.dbenv, 0); - if (dbA.remove(strFile.c_str(), NULL, 0)) + if (dbA.remove(strFile.c_str(), nullptr, 0)) fSuccess = false; Db dbB(&bitdb.dbenv, 0); - if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0)) fSuccess = false; } if (!fSuccess) diff --git a/src/wallet/db.h b/src/wallet/db.h index bdaff79c05..6f519337b9 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -80,10 +80,10 @@ class CDBEnv DbTxn *TxnBegin(int flags=DB_TXN_WRITE_NOSYNC) { - DbTxn* ptxn = NULL; - int ret = dbenv.txn_begin(NULL, &ptxn, flags); + DbTxn* ptxn = nullptr; + int ret = dbenv.txn_begin(nullptr, &ptxn, flags); if (!ptxn || ret != 0) - return NULL; + return nullptr; return ptxn; } }; @@ -128,7 +128,7 @@ class CDB datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(activeTxn, &datKey, &datValue, 0); memset(datKey.get_data(), 0, datKey.get_size()); - if (datValue.get_data() == NULL) + if (datValue.get_data() == nullptr) return false; // Unserialize value @@ -220,11 +220,11 @@ class CDB Dbc* GetCursor() { if (!pdb) - return NULL; - Dbc* pcursor = NULL; - int ret = pdb->cursor(NULL, &pcursor, 0); + return nullptr; + Dbc* pcursor = nullptr; + int ret = pdb->cursor(nullptr, &pcursor, 0); if (ret != 0) - return NULL; + return nullptr; return pcursor; } @@ -248,7 +248,7 @@ class CDB int ret = pcursor->get(&datKey, &datValue, fFlags); if (ret != 0) return ret; - else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) return 99999; // Convert to streams @@ -284,7 +284,7 @@ class CDB if (!pdb || !activeTxn) return false; int ret = activeTxn->commit(0); - activeTxn = NULL; + activeTxn = nullptr; return (ret == 0); } @@ -293,7 +293,7 @@ class CDB if (!pdb || !activeTxn) return false; int ret = activeTxn->abort(); - activeTxn = NULL; + activeTxn = nullptr; return (ret == 0); } @@ -308,7 +308,7 @@ class CDB return Write(std::string("version"), nVersion); } - bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); + bool static Rewrite(const std::string& strFile, const char* pszSkip = nullptr); }; #endif // BITCOIN_DB_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 507559615e..c15575510c 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -88,9 +88,9 @@ class CTxDump bool fSpent; CWalletTx* ptx; int nOut; - CTxDump(CWalletTx* ptx = NULL, int nOut = -1) + CTxDump(CWalletTx* ptx = nullptr, int nOut = -1) { - pindex = NULL; + pindex = nullptr; nValue = 0; fSpent = false; this->ptx = ptx; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 651abffd88..380ab13adb 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1626,10 +1626,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp) for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx* const pwtx = it->second.first; - if (pwtx != 0) + if (pwtx != nullptr) ListTransactions(*pwtx, strAccount, 0, true, ret, filter); CAccountingEntry* const pacentry = it->second.second; - if (pacentry != 0) + if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret); if ((int)ret.size() >= (nCount+nFrom)) break; @@ -1693,10 +1693,10 @@ UniValue liststakes(const UniValue& params, bool fHelp) for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx *const pwtx = it->second.first; - if (pwtx != 0) + if (pwtx != nullptr) ListTransactions(*pwtx, strAccount, 0, true, ret_superset, filter, true); CAccountingEntry *const pacentry = it->second.second; - if (pacentry != 0) + if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret_superset); if ((int)ret_superset.size() >= nCount) break; @@ -1799,7 +1799,7 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBlockIndex *pindex = NULL; + CBlockIndex* pindex = nullptr; int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; if (params.size() > 0) @@ -2181,7 +2181,7 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) "walletpassphrase \n" "Stores the wallet decryption key in memory for seconds."); - NewThread(ThreadTopUpKeyPool, NULL); + NewThread(ThreadTopUpKeyPool, nullptr); int64_t* pnSleepTime = new int64_t(nSleepTime); NewThread(ThreadCleanWalletPassphrase, pnSleepTime); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 54d3b19493..0a1def3df1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -364,7 +364,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. delete pwalletdbEncryption; - pwalletdbEncryption = NULL; + pwalletdbEncryption = nullptr; } Lock(); @@ -407,13 +407,13 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { CWalletTx* wtx = &(it->second); - txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); + txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, nullptr))); } acentries.clear(); walletdb.ListAccountCreditDebit(strAccount, acentries); for (auto &entry : acentries) { - txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + txOrdered.insert(make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); } return txOrdered; @@ -1490,7 +1490,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, // List of values less than target pair > coinLowestLarger; coinLowestLarger.first = std::numeric_limits::max(); - coinLowestLarger.second.first = NULL; + coinLowestLarger.second.first = nullptr; vector > > vValue; int64_t nTotalLower = 0; @@ -1543,7 +1543,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, if (nTotalLower < nTargetValue) { - if (coinLowestLarger.second.first == NULL) + if (coinLowestLarger.second.first == nullptr) return false; setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; @@ -2073,7 +2073,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) // This is only to keep the database open to defeat the auto-flush for the // duration of this scope. This is the only place where this optimization // maybe makes sense; please don't do it anywhere else. - CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r+") : NULL; + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile, "r+") : nullptr; // Take key pair from key pool so it won't be used again reservekey.KeepKey(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 881ecba0cb..30dd197a03 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -81,7 +81,7 @@ class CKeyPool class CWallet : public CCryptoKeyStore { private: - bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl=NULL, bool contract = false) const; + bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set>& setCoinsRet, int64_t& nValueRet, const CCoinControl* coinControl = nullptr, bool contract = false) const; CWalletDB *pwalletdbEncryption; @@ -171,7 +171,7 @@ class CWallet : public CCryptoKeyStore nWalletMaxVersion = FEATURE_BASE; fFileBacked = false; nMasterKeyMaxID = 0; - pwalletdbEncryption = NULL; + pwalletdbEncryption = nullptr; nOrderPosNext = 0; nTimeFirstKey = 0; } @@ -191,7 +191,7 @@ class CWallet : public CCryptoKeyStore void AvailableCoinsForStaking(std::vector& vCoins, unsigned int nSpendTime, int64_t& nBalanceOut) const; bool SelectCoinsForStaking(unsigned int nSpendTime, std::vector >& vCoinsRet, GRC::MinerStatus::ReasonNotStakingCategory& not_staking_error, int64_t& balance_out, bool fMiner = false) const; - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL, bool fIncludeStakingCoins=false) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl* coinControl = nullptr, bool fIncludeStakingCoins = false) const; bool SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; bool SelectSmallestCoins(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; @@ -224,7 +224,7 @@ class CWallet : public CCryptoKeyStore /** Increment the next transaction order id @return next transaction order id */ - int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); + int64_t IncOrderPosNext(CWalletDB* pwalletdb = nullptr); typedef std::pair TxPair; typedef std::multimap TxItems; @@ -248,9 +248,9 @@ class CWallet : public CCryptoKeyStore int64_t GetImmatureBalance() const; int64_t GetStake() const; int64_t GetNewMint() const; - bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL); - bool CreateTransaction(const std::vector >& vecSend, std::set>& setCoins, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL); - bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL); + bool CreateTransaction(const std::vector>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); + bool CreateTransaction(const std::vector>& vecSend, std::set>& setCoins, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); + bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); @@ -370,7 +370,7 @@ class CWallet : public CCryptoKeyStore bool SetDefaultKey(const CPubKey &vchPubKey); // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower - bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = nullptr, bool fExplicit = false); // change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) bool SetMaxVersion(int nVersion); @@ -497,7 +497,7 @@ class CWalletTx : public CMerkleTx CWalletTx() { - Init(NULL); + Init(nullptr); } CWalletTx(const CWallet* pwalletIn) @@ -550,7 +550,7 @@ class CWalletTx : public CMerkleTx char fSpent = false; if (ser_action.ForRead()) { - Init(NULL); + Init(nullptr); } else { mapValue["fromaccount"] = strFromAccount; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 79191b4c97..de40058a14 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -138,13 +138,13 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { CWalletTx* wtx = &(it->second); - txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); } list acentries; ListAccountCreditDebit("", acentries); for (auto &entry : acentries) { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + txByTime.insert(make_pair(entry.nTime, TxPair(nullptr, &entry))); } int64_t& nOrderPosNext = pwallet->nOrderPosNext; @@ -154,7 +154,7 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) { CWalletTx* const pwtx = it->second.first; CAccountingEntry* const pacentry = it->second.second; - int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + int64_t& nOrderPos = (pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) { @@ -702,7 +702,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) int64_t now = GetTime(); std::string newFilename = strprintf("wallet.%" PRId64 ".bak", now); - int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, + int result = dbenv.dbenv.dbrename(nullptr, filename.c_str(), nullptr, newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) LogPrintf("Renamed %s to %s", filename, newFilename); @@ -724,11 +724,11 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) bool fSuccess = allOK; //Db* pdbCopy = new Db(&dbenv.dbenv, 0); boost::scoped_ptr pdbCopy(new Db(&dbenv.dbenv, 0)); - int ret = pdbCopy->open(NULL, // Txn pointer - filename.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - DB_CREATE, // Flags + int ret = pdbCopy->open(nullptr, // Txn pointer + filename.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags 0); if (ret > 0) { From bfbfa5317399951b893222b8b5ee13b0f4231dfe Mon Sep 17 00:00:00 2001 From: jamescowens Date: Thu, 3 Jun 2021 19:44:02 -0400 Subject: [PATCH 194/280] Refresh rainbymagnitude This fixes the magnitude rounding to agree with the same rounding method used in magnitude storage in the superblock. It also implements a dust suppressor by suppressing payments less than one CENT and renormalizing the remaining payments. It removes the ability to provide a custom message, because of prior abuse, and it also implements a minimum requirement of 1000 GRC. --- src/rpc/blockchain.cpp | 198 ++++++++++++++++++++++++++++++----------- src/rpc/client.cpp | 2 + 2 files changed, 146 insertions(+), 54 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 40f49bfbdc..6b0ea21c02 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -545,31 +545,50 @@ UniValue backupprivatekeys(const UniValue& params, bool fHelp) UniValue rainbymagnitude(const UniValue& params, bool fHelp) { - if (fHelp || (params.size() < 2 || params.size() > 3)) + if (fHelp || (params.size() < 2 || params.size() > 4)) throw runtime_error( - "rainbymagnitude [message]\n" + "rainbymagnitude project_id amount ( trial_run output_details )\n" "\n" - " --> Required: If a project is specified, rain will be limited to that project. Use * for network-wide.\n" - " --> Required: Specify amount of coins to be rained in double precision float\n" - "[message] -> Optional: Provide a message rained to all rainees\n" + "project_id -> Required: Limits rain to a specific project. Use \"*\" for\n" + " network-wide. Call \"listprojects\" for the IDs of eligible\n" + " projects." + "amount -> Required: Amount to rain (1000 GRC minimum).\n" + "trial_run -> Optional: Boolean to specify a trial run instead of an actual\n" + " transaction (default: false).\n" + "output_details -> Optional: Boolean to output recipient details (default: false\n" + " if not trial run, true if trial run).\n" "\n" "rain coins by magnitude on network"); UniValue res(UniValue::VOBJ); + UniValue details(UniValue::VARR); std::string sProject = params[0].get_str(); LogPrint(BCLog::LogFlags::VERBOSE, "rainbymagnitude: sProject = %s", sProject.c_str()); - double dAmount = params[1].get_real(); + CAmount amount = AmountFromValue(params[1]); - if (dAmount <= 0) - throw runtime_error("Amount must be greater then 0"); + if (amount < 1000 * COIN) + { + throw runtime_error("Minimum amount to rain is 1000 GRC."); + } - std::string sMessage = ""; + std::string sMessage; + + bool trial_run = false; if (params.size() > 2) - sMessage = params[2].get_str(); + { + trial_run = params[2].get_bool(); + } + + bool output_details = trial_run; + + if (params.size() > 3) + { + output_details = params[3].get_bool(); + } // Make sure statistics are up to date. This will do nothing if a convergence has already been cached and is clean. bool bStatsAvail = ScraperGetSuperblockContract(false, false).WellFormed(); @@ -589,8 +608,8 @@ UniValue rainbymagnitude(const UniValue& params, bool fHelp) LogPrint(BCLog::LogFlags::VERBOSE, "rainbymagnitude: mScraperConvergedStats size = %u", mScraperConvergedStats.size()); - double dTotalAmount = 0; - int64_t nTotalAmount = 0; + CAmount total_amount_1st_pass = 0; + CAmount total_amount_2nd_pass = 0; double dTotalMagnitude = 0; @@ -598,8 +617,8 @@ UniValue rainbymagnitude(const UniValue& params, bool fHelp) const int64_t now = GetAdjustedTime(); // Time to calculate beacon expiration from - //------- CPID ------------- beacon address -- Mag - std::map> mCPIDRain; + //------- CPID -------------- beacon address -- Mag --- payment - suppressed + std::map> mCPIDRain; for (const auto& entry : mScraperConvergedStats) { @@ -622,13 +641,15 @@ UniValue rainbymagnitude(const UniValue& params, bool fHelp) CPIDKey = GRC::Cpid::Parse(entry.first.objectID); } - double dCPIDMag = std::round(entry.second.statsvalue.dMag); + double dCPIDMag = GRC::Magnitude::RoundFrom(entry.second.statsvalue.dMag).Floating(); // Zero mag CPIDs do not get paid. if (!dCPIDMag) continue; CBitcoinAddress address; + // If the beacon is active get the address and insert an entry into the map for payment, + // otherwise skip. if (const GRC::BeaconOption beacon = GRC::GetBeaconRegistry().TryActive(CPIDKey, now)) { address = beacon->GetAddress(); @@ -638,60 +659,116 @@ UniValue rainbymagnitude(const UniValue& params, bool fHelp) continue; } - LogPrint(BCLog::LogFlags::VERBOSE, "INFO: rainbymagnitude: address = %s.", address.ToString()); - - mCPIDRain[CPIDKey] = std::make_pair(address, dCPIDMag); + // The last two elements of the tuple will be filled out when doing the passes for payment. + mCPIDRain[CPIDKey] = std::make_tuple(address, dCPIDMag, CAmount {0}, false); // Increment the accumulated mag. This will be equal to the total mag of the valid CPIDs entered // into the RAIN map, and will be used to normalize the payments. dTotalMagnitude += dCPIDMag; - - LogPrint(BCLog::LogFlags::VERBOSE, "rainmagnitude: CPID = %s, address = %s, dCPIDMag = %f", - CPIDKey.ToString(), address.ToString(), dCPIDMag); } } if (mCPIDRain.empty() || !dTotalMagnitude) { - throw JSONRPCError(RPC_MISC_ERROR, "No CPIDs to pay and/or total CPID magnitude is zero. This could be caused by an incorrect project specified."); + throw JSONRPCError(RPC_MISC_ERROR, "No CPIDs to pay and/or total CPID magnitude is zero. This could be caused by an " + "incorrect project specified."); } - std::vector > vecSend; + std::vector > vecSend; + unsigned int subcent_suppression_count = 0; + + for (auto& iter : mCPIDRain) + { + double& dCPIDMag = std::get<1>(iter.second); + + CAmount payment = roundint64((double) amount * dCPIDMag / dTotalMagnitude); - // Setup the payment vector now that the CPID entries and mags have been validated and the total mag is computed. - for(const auto& iter : mCPIDRain) + std::get<2>(iter.second) = payment; + + // Do not allow payments less than one cent to prevent dust in the network. + if (payment < CENT) + { + std::get<3>(iter.second) = true; + + ++subcent_suppression_count; + continue; + } + + total_amount_1st_pass += payment; + } + + // Because we are suppressing payments of less than one cent to CPIDs, we need to renormalize the payments to ensure + // the full amount is disbursed to the surviving CPID payees. This is going to be a small amount, but is worth doing. + for (const auto& iter : mCPIDRain) { - double dCPIDMag = iter.second.second; + // Make it easier to read. + const GRC::Cpid& cpid = iter.first; + const CBitcoinAddress& address = std::get<0>(iter.second); + const double& magnitude = std::get<1>(iter.second); - double dPayout = (dCPIDMag / dTotalMagnitude) * dAmount; + // This is not a const reference on purpose because it has to be renormalized. + CAmount payment = std::get<2>(iter.second); + const bool& suppressed = std::get<3>(iter.second); - dTotalAmount += dPayout; + // Note the cast to double ensures that the calculations are reasonable over a wide dynamic range, without risk of + // overflow on a large iter.second and amount without using bignum math. The slight loss of precision here is not + // important. As above, with the renormalization here, we will round to the nearest Halford rather than truncating. + payment = roundint64((double) payment * (double) amount / (double) total_amount_1st_pass); - CScript scriptPubKey; - scriptPubKey.SetDestination(iter.second.first.Get()); + CScript scriptPubKey; + scriptPubKey.SetDestination(address.Get()); - int64_t nAmount = roundint64(dPayout * COIN); - nTotalAmount += nAmount; + LogPrint(BCLog::LogFlags::VERBOSE, "INFO: %s: cpid = %s. address = %s, magnitude = %.2f, " + "payment = %s, dust_suppressed = %u", + __func__, cpid.ToString(), address.ToString(), magnitude, ValueFromAmount(payment).getValStr(), suppressed); - vecSend.push_back(std::make_pair(scriptPubKey, nAmount)); + if (output_details) + { + UniValue detail_entry(UniValue::VOBJ); + + detail_entry.pushKV("cpid", cpid.ToString()); + detail_entry.pushKV("address", address.ToString()); + detail_entry.pushKV("magnitude", magnitude); + detail_entry.pushKV("amount", ValueFromAmount(payment)); + detail_entry.pushKV("suppressed", suppressed); - LogPrint(BCLog::LogFlags::VERBOSE, "rainmagnitude: address = %s, amount = %f", iter.second.first.ToString(), CoinToDouble(nAmount)); + details.push_back(detail_entry); + } + + // If dust suppression flag is false, add to payment vector for sending. + if (!suppressed) + { + vecSend.push_back(std::make_pair(scriptPubKey, payment)); + total_amount_2nd_pass += payment; + } + } + + if (total_amount_2nd_pass <= 0) + { + throw JSONRPCError(RPC_MISC_ERROR, "No payments above 0.01 GRC qualify. Please recheck your specified amount."); } LOCK2(cs_main, pwalletMain->cs_wallet); CWalletTx wtx; wtx.mapValue["comment"] = "Rain By Magnitude"; + + // Custom messages are no longer supported. The static "rain by magnitude" message will be replaced by an actual + // rain contract at the next mandatory. wtx.vContracts.emplace_back(GRC::MakeContract( GRC::ContractAction::ADD, - "Rain By Magnitude: " + sMessage)); + "Rain By Magnitude")); EnsureWalletIsUnlocked(); + // Check funds - double dBalance = pwalletMain->GetBalance(); + CAmount balance = pwalletMain->GetBalance(); + + if (total_amount_2nd_pass > balance) + { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds for specified rain."); + } - if (dTotalAmount > dBalance) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); // Send CReserveKey keyChange(pwalletMain); @@ -702,28 +779,41 @@ UniValue rainbymagnitude(const UniValue& params, bool fHelp) if (!fCreated) { - if (nTotalAmount + nFeeRequired > pwalletMain->GetBalance()) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + if (total_amount_2nd_pass + nFeeRequired > balance) + { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds for specified rain."); + } throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed"); } - LogPrintf("Committing."); - // Rain the recipients - if (!pwalletMain->CommitTransaction(wtx, keyChange)) + if (!trial_run) { - LogPrintf("Rain By Magnitude Commit failed."); + // Rain the recipients + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + { + error("%s: Rain by magnitude transaction commit failed.", __func__); - throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); -} - res.pushKV("Rain By Magnitude", "Sent"); - res.pushKV("TXID", wtx.GetHash().GetHex()); - res.pushKV("Rain Amount Sent", dTotalAmount); - res.pushKV("TX Fee", ValueFromAmount(nFeeRequired)); - res.pushKV("# of Recipients", (uint64_t)vecSend.size()); - - if (!sMessage.empty()) - res.pushKV("Message", sMessage); + throw JSONRPCError(RPC_WALLET_ERROR, "Rain by magnitude transaction commit failed."); + } + + res.pushKV("status", "transaction sent"); + res.pushKV("txid", wtx.GetHash().GetHex()); + } + else + { + res.pushKV("status", "trial run - nothing sent"); + } + + res.pushKV("amount", ValueFromAmount(total_amount_2nd_pass)); + res.pushKV("fee", ValueFromAmount(nFeeRequired)); + res.pushKV("recipients", (uint64_t) vecSend.size()); + res.pushKV("suppressed_subcent_recipients", (uint64_t) subcent_suppression_count); + + if (output_details) + { + res.pushKV("recipient_details", details); + } return res; } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 82958741f1..3c893a386c 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -148,6 +148,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "move" , 2 }, { "move" , 3 }, { "rainbymagnitude" , 1 }, + { "rainbymagnitude" , 2 }, + { "rainbymagnitude" , 3 }, { "reservebalance" , 0 }, { "reservebalance" , 1 }, { "scanforunspent" , 1 }, From cb6105dcee40b6673fba859fd30ab649ad09eafc Mon Sep 17 00:00:00 2001 From: Cy Rossignol Date: Sun, 13 Jun 2021 12:21:57 -0500 Subject: [PATCH 195/280] Fix GUI display artifact in poll loading indicator The progress bar text peeked out from the bottom of the loading indicator and looks like a rendering artifact. --- src/qt/voting/polltab.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/voting/polltab.cpp b/src/qt/voting/polltab.cpp index e612d8dd4e..2058110a12 100644 --- a/src/qt/voting/polltab.cpp +++ b/src/qt/voting/polltab.cpp @@ -52,6 +52,7 @@ class LoadingBar : public QProgressBar , m_active(false) { setRange(0, MAX); + setTextVisible(false); setGraphicsEffect(&m_opacity_effect); hide(); From ef61d6a88fdf35ca78c545cfbde7c17be3d159e2 Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sat, 5 Jun 2021 21:46:44 -0400 Subject: [PATCH 196/280] Implement GUI settings for stakesplitting This commit builds on the implementation of the ArgsManager and implements GUi settings in a new staking tab in the options dialog to dynamically control the status of stake splitting for staking optimization, and the associated settings, the staking efficiency and the minimum post stake split UTXO value. Validation is performed on the fields to ensure that the efficiency is between 75% and 98%, and the minstakesplitvalue is 800 or greater. Note that we are making use of the new updateRwSetting implemented with the ArgsManager port, which saves runtime settings to a read-write JSON settings file. Settings in this file take precedence over the read-only settings in the config file. The tool tips warn the user about this order-of-precedence (override), so they are not confused later. This also includes a check-box to disable/enable staking. --- src/gridcoin/staking/status.cpp | 1 + src/gridcoin/staking/status.h | 1 + src/init.cpp | 10 +-- src/miner.cpp | 11 ++- src/net.cpp | 8 +- src/qt/bitcoingui.cpp | 18 +++-- src/qt/forms/optionsdialog.ui | 86 ++++++++++++++++++++- src/qt/optionsdialog.cpp | 128 ++++++++++++++++++++++++++++++-- src/qt/optionsdialog.h | 7 ++ src/qt/optionsmodel.cpp | 38 ++++++++++ src/qt/optionsmodel.h | 6 +- src/util/system.h | 3 +- 12 files changed, 289 insertions(+), 28 deletions(-) diff --git a/src/gridcoin/staking/status.cpp b/src/gridcoin/staking/status.cpp index df3578c343..5f1f718545 100644 --- a/src/gridcoin/staking/status.cpp +++ b/src/gridcoin/staking/status.cpp @@ -18,6 +18,7 @@ namespace { //! constexpr const char* STAKING_ERROR_STRINGS[] { "None", + "Commanded disabled", "No Mature Coins", "No coins", "Entire balance reserved", diff --git a/src/gridcoin/staking/status.h b/src/gridcoin/staking/status.h index a097226ae9..1c2728d1c6 100644 --- a/src/gridcoin/staking/status.h +++ b/src/gridcoin/staking/status.h @@ -20,6 +20,7 @@ class MinerStatus enum ReasonNotStakingCategory { NONE, + COMMANDED_DISABLED, NO_MATURE_COINS, NO_COINS, ENTIRE_BALANCE_RESERVED, diff --git a/src/init.cpp b/src/init.cpp index 03d6e10ab2..7c3e53aea6 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -357,9 +357,9 @@ void SetupServerArgs() // Staking argsman.AddArg("-enablesidestaking", "Enable side staking functionality (default: 0)", - ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); argsman.AddArg("-staking", "Allow wallet to stake if conditions to stake are met (default: 1)", - ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); argsman.AddArg("-sidestake=", "Sidestake destination and allocation entry. There can be as many " "specified as desired. Only six per stake can be sent. If more than " "six are specified. Six are randomly chosen for each stake. Only active " @@ -367,13 +367,13 @@ void SetupServerArgs() ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); argsman.AddArg("-enablestakesplit", "Enable unspent output spitting when staking to optimize staking efficiency " "(default: 0", - ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); argsman.AddArg("-stakingefficiency=", "Specify target staking efficiency for stake splitting (default: 90, " "clamped to [75, 98])", - ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); argsman.AddArg("-minstakesplitvalue=", strprintf("Specify minimum output value for post split output when stake " "splitting (default: %" PRId64 "GRC)", MIN_STAKE_SPLIT_VALUE_GRC), - ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); // Scraper argsman.AddArg("-scraper", "Activate scraper for statistics downloads. This will only work if the node has a wallet " diff --git a/src/miner.cpp b/src/miner.cpp index 55e5b02061..443b914397 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1156,14 +1156,14 @@ bool IsMiningAllowed(CWallet *pwallet) { bool status = true; - if(pwallet->IsLocked()) + if (pwallet->IsLocked()) { LOCK(g_miner_status.lock); g_miner_status.SetReasonNotStaking(GRC::MinerStatus::WALLET_LOCKED); status = false; } - if(fDevbuildCripple) + if (fDevbuildCripple) { LOCK(g_miner_status.lock); g_miner_status.SetReasonNotStaking(GRC::MinerStatus::TESTNET_ONLY); @@ -1177,6 +1177,13 @@ bool IsMiningAllowed(CWallet *pwallet) status = false; } + if (!gArgs.GetBoolArg("-staking", true)) + { + LOCK(g_miner_status.lock); + g_miner_status.SetReasonNotStaking(GRC::MinerStatus::COMMANDED_DISABLED); + status = false; + } + return status; } diff --git a/src/net.cpp b/src/net.cpp index 88bedb2c72..3b8a985857 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2032,12 +2032,8 @@ void StartNode(void* parg) if (!netThreads->createThread(ThreadDumpAddress,NULL,"ThreadDumpAddress")) LogPrintf("Error: createThread(ThreadDumpAddress) failed"); - // Mine proof-of-stake blocks in the background - if (!gArgs.GetBoolArg("-staking", true)) - LogPrintf("Staking disabled"); - else - if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner")) - LogPrintf("Error: createThread(ThreadStakeMiner) failed"); + if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner")) + LogPrintf("Error: createThread(ThreadStakeMiner) failed"); } bool StopNode() diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index d6776852cc..fabbf9f362 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -691,16 +691,22 @@ void BitcoinGUI::createToolBars() //12-21-2015 Prevent Lock from falling off the page frameBlocksLayout->addStretch(); + QTimer *timerStakingIcon = new QTimer(labelStakingIcon); + connect(timerStakingIcon, SIGNAL(timeout()), this, SLOT(updateStakingIcon())); + timerStakingIcon->start(MODEL_UPDATE_DELAY); + + // Instead of calling updateStakingIcon here, simply set the icon to staking off. + // This is to prevent problems since this GUI code can initialize before the core. + labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_no_" + sSheet)); + if (gArgs.GetBoolArg("-staking", true)) { - QTimer *timerStakingIcon = new QTimer(labelStakingIcon); - connect(timerStakingIcon, SIGNAL(timeout()), this, SLOT(updateStakingIcon())); - timerStakingIcon->start(MODEL_UPDATE_DELAY); - // Instead of calling updateStakingIcon here, simply set the icon to staking off. - // This is to prevent problems since this GUI code can initialize before the core. - labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_no_" + sSheet)); labelStakingIcon->setToolTip(tr("Not staking: Miner is not initialized.")); } + else + { + labelStakingIcon->setToolTip(tr("Not staking: Commanded disabled.")); + } statusBar()->addPermanentWidget(frameBlocks); diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 43bbb533f0..a8ac9b801b 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 540 - 400 + 709 + 421 @@ -254,6 +254,88 @@ + + + Staking + + + + + 10 + 10 + 651 + 135 + + + + + + + This enables or disables staking (the default is enabled). Note that a change to this setting will permanently override the config file with an entry in the settings file. + + + Enable Staking + + + + + + + This enables or disables splitting of stake outputs to optimize staking (default disabled). Note that a change to this setting will permanently override the config file with an entry in the settings file. + + + Enable Stake Splitting + + + + + + + + + Target Efficiency + + + + + + + Valid values are between 75 and 98 percent. Note that a change to this setting will permanently override the config file with an entry in the settings file. + + + + + + + Min Post Split UTXO + + + + + + + Valid values are 800 or greater. Note that a change to this setting will permanently override the config file with an entry in the settings file. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Window diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 2d85a2a12f..42c4b7a471 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -6,6 +6,7 @@ #include "monitoreddatamapper.h" #include "optionsmodel.h" #include "init.h" +#include "miner.h" #include #include @@ -45,6 +46,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) : connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Proxy())); ui->proxyIp->installEventFilter(this); + ui->stakingEfficiency->installEventFilter(this); + ui->minPostSplitOutputValue->installEventFilter(this); /* Window elements init */ #ifdef Q_OS_MAC @@ -86,8 +89,13 @@ OptionsDialog::OptionsDialog(QWidget *parent) : connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApplyButton())); /* disable apply button when new data loaded */ connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApplyButton())); - /* setup/change UI elements when proxy IP is invalid/valid */ - connect(this, SIGNAL(proxyIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleProxyIpValid(QValidatedLineEdit *, bool))); + /* setup/change UI elements when proxy IP, stakingEfficiency, or minStakeSplitValue is invalid/valid */ + connect(this, SIGNAL(proxyIpValid(QValidatedLineEdit *, bool)), + this, SLOT(handleProxyIpValid(QValidatedLineEdit *, bool))); + connect(this, SIGNAL(stakingEfficiencyValid(QValidatedLineEdit *, bool)), + this, SLOT(handleStakingEfficiencyValid(QValidatedLineEdit *, bool))); + connect(this, SIGNAL(minStakeSplitValueValid(QValidatedLineEdit *, bool)), + this, SLOT(handleMinStakeSplitValueValid(QValidatedLineEdit *, bool))); if (fTestNet) ui->disableUpdateCheck->setHidden(true); @@ -98,6 +106,15 @@ OptionsDialog::OptionsDialog(QWidget *parent) : connect(ui->gridcoinAtStartupMinimised, SIGNAL(toggled(bool)), this, SLOT(hideStartMinimized())); connect(ui->limitTxnDisplayCheckBox, SIGNAL(toggled(bool)), this, SLOT(hideLimitTxnDisplayDate())); + + bool stake_split_enabled = ui->enableStakeSplit->isChecked(); + + ui->stakingEfficiencyLabel->setHidden(!stake_split_enabled); + ui->stakingEfficiency->setHidden(!stake_split_enabled); + ui->minPostSplitOutputValueLabel->setHidden(!stake_split_enabled); + ui->minPostSplitOutputValue->setHidden(!stake_split_enabled); + + connect(ui->enableStakeSplit, SIGNAL(toggled(bool)), this, SLOT(hideStakeSplitting())); } OptionsDialog::~OptionsDialog() @@ -146,6 +163,12 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort); mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion); + /* Staking */ + mapper->addMapping(ui->enableStaking, OptionsModel::EnableStaking); + mapper->addMapping(ui->enableStakeSplit, OptionsModel::EnableStakeSplit); + mapper->addMapping(ui->stakingEfficiency, OptionsModel::StakingEfficiency); + mapper->addMapping(ui->minPostSplitOutputValue, OptionsModel::MinStakeSplitValue); + /* Window */ mapper->addMapping(ui->disableTransactionNotifications, OptionsModel::DisableTrxNotifications); mapper->addMapping(ui->disablePollNotifications, OptionsModel::DisablePollNotifications); @@ -264,12 +287,25 @@ void OptionsDialog::hideLimitTxnDisplayDate() } } +void OptionsDialog::hideStakeSplitting() +{ + if (model) + { + bool stake_split_enabled = ui->enableStakeSplit->isChecked(); + + ui->stakingEfficiencyLabel->setHidden(!stake_split_enabled); + ui->stakingEfficiency->setHidden(!stake_split_enabled); + ui->minPostSplitOutputValueLabel->setHidden(!stake_split_enabled); + ui->minPostSplitOutputValue->setHidden(!stake_split_enabled); + } +} + void OptionsDialog::handleProxyIpValid(QValidatedLineEdit *object, bool fState) { // this is used in a check before re-enabling the save buttons fProxyIpValid = fState; - if(fProxyIpValid) + if (fProxyIpValid) { enableSaveButtons(); ui->statusLabel->clear(); @@ -283,16 +319,98 @@ void OptionsDialog::handleProxyIpValid(QValidatedLineEdit *object, bool fState) } } +void OptionsDialog::handleStakingEfficiencyValid(QValidatedLineEdit *object, bool fState) +{ + // this is used in a check before re-enabling the save buttons + fStakingEfficiencyValid = fState; + + if (fStakingEfficiencyValid) + { + enableSaveButtons(); + ui->statusLabel->clear(); + } + else + { + disableSaveButtons(); + object->setValid(fStakingEfficiencyValid); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The supplied target staking efficiency is invalid.")); + } +} + +void OptionsDialog::handleMinStakeSplitValueValid(QValidatedLineEdit *object, bool fState) +{ + // this is used in a check before re-enabling the save buttons + fMinStakeSplitValueValid = fState; + + if (fMinStakeSplitValueValid) + { + enableSaveButtons(); + ui->statusLabel->clear(); + } + else + { + disableSaveButtons(); + object->setValid(fMinStakeSplitValueValid); + ui->statusLabel->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel->setText(tr("The supplied minimum post stake-split UTXO size is invalid.")); + } +} + bool OptionsDialog::eventFilter(QObject *object, QEvent *event) { - if(event->type() == QEvent::FocusOut) + if (event->type() == QEvent::FocusOut) { - if(object == ui->proxyIp) + if (object == ui->proxyIp) { CService addr; /* Check proxyIp for a valid IPv4/IPv6 address and emit the proxyIpValid signal */ emit proxyIpValid(ui->proxyIp, LookupNumeric(ui->proxyIp->text().toStdString().c_str(), addr)); } + + if (object == ui->stakingEfficiency) + { + bool ok = false; + double efficiency = ui->stakingEfficiency->text().toDouble(&ok); + + if (!ok) + { + emit stakingEfficiencyValid(ui->stakingEfficiency, false); + } + else + { + if (efficiency < 75.0 || efficiency > 98.0) + { + emit stakingEfficiencyValid(ui->stakingEfficiency, false); + } + else + { + emit stakingEfficiencyValid(ui->stakingEfficiency, true); + } + } + } + + if (object == ui->minPostSplitOutputValue) + { + bool ok = false; + CAmount post_split_min_value = (CAmount) ui->minPostSplitOutputValue->text().toULong(&ok) * COIN; + + if (!ok) + { + emit minStakeSplitValueValid(ui->minPostSplitOutputValue, false); + } + else + { + if (post_split_min_value < MIN_STAKE_SPLIT_VALUE_GRC * COIN || post_split_min_value > MAX_MONEY) + { + emit minStakeSplitValueValid(ui->minPostSplitOutputValue, false); + } + else + { + emit minStakeSplitValueValid(ui->minPostSplitOutputValue, true); + } + } + } } return QDialog::eventFilter(object, event); } diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 4509795bdd..79a362830f 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -46,10 +46,15 @@ private slots: void updateStyle(); void hideStartMinimized(); void hideLimitTxnDisplayDate(); + void hideStakeSplitting(); void handleProxyIpValid(QValidatedLineEdit *object, bool fState); + void handleStakingEfficiencyValid(QValidatedLineEdit *object, bool fState); + void handleMinStakeSplitValueValid(QValidatedLineEdit *object, bool fState); signals: void proxyIpValid(QValidatedLineEdit *object, bool fValid); + void stakingEfficiencyValid(QValidatedLineEdit *object, bool fValid); + void minStakeSplitValueValid(QValidatedLineEdit *object, bool fValid); private: Ui::OptionsDialog *ui; @@ -58,6 +63,8 @@ private slots: bool fRestartWarningDisplayed_Proxy; bool fRestartWarningDisplayed_Lang; bool fProxyIpValid; + bool fStakingEfficiencyValid; + bool fMinStakeSplitValueValid; }; #endif // OPTIONSDIALOG_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index a8014f2db8..b5757081b2 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,5 +1,6 @@ #include "optionsmodel.h" #include "bitcoinunits.h" +#include "miner.h" #include #include @@ -142,6 +143,18 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(gArgs.GetBoolArg("-disableupdatecheck", false)); case DataDir: return settings.value("dataDir", QString::fromStdString(gArgs.GetArg("-datadir", GetDataDir().string()))); + case EnableStaking: + // This comes from the core and is a read-write setting (see below). + return QVariant(gArgs.GetBoolArg("-staking", true)); + case EnableStakeSplit: + // This comes from the core and is a read-write setting (see below). + return QVariant(gArgs.GetBoolArg("-enablestakesplit")); + case StakingEfficiency: + // This comes from the core and is a read-write setting (see below). + return QVariant((double) gArgs.GetArg("-stakingefficiency", (int64_t) 90)); + case MinStakeSplitValue: + // This comes from the core and is a read-write setting (see below). + return QVariant((qint64) gArgs.GetArg("-minstakesplitvalue", MIN_STAKE_SPLIT_VALUE_GRC)); default: return QVariant(); } @@ -275,6 +288,31 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in // be changed while the wallet is running. dataDir = value.toString(); settings.setValue("dataDir", dataDir); + break; + case EnableStaking: + // This is a core setting stored in the read-write settings file and once set will override the read-only + //config file. + gArgs.ForceSetArg("-staking", value.toBool() ? "1" : "0"); + updateRwSetting("staking", gArgs.GetBoolArg("-staking", true)); + break; + case EnableStakeSplit: + // This is a core setting stored in the read-write settings file and once set will override the read-only + //config file. + //fStakeSplitEnabled = value.toBool(); + gArgs.ForceSetArg("-enablestakesplit", value.toBool() ? "1" : "0"); + updateRwSetting("enablestakesplit", gArgs.GetBoolArg("-enablestakesplit")); + break; + case StakingEfficiency: + // This is a core setting stored in the read-write settings file and once set will override the read-only + //config file. + gArgs.ForceSetArg("-stakingefficiency", value.toString().toStdString()); + updateRwSetting("stakingefficiency", gArgs.GetArg("-stakingefficiency", 90)); + break; + case MinStakeSplitValue: + // This is a core setting stored in the read-write settings file and once set will override the read-only + //config file. + gArgs.ForceSetArg("-minstakesplitvalue", value.toString().toStdString()); + updateRwSetting("minstakesplitvalue", gArgs.GetArg("-minstakesplitvalue", MIN_STAKE_SPLIT_VALUE_GRC)); default: break; } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 6d77b0a794..c940cb02fe 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -39,7 +39,11 @@ class OptionsModel : public QAbstractListModel LimitTxnDate, // QDate DisableUpdateCheck, // bool DataDir, // QString - OptionIDRowCount, + EnableStaking, // bool + EnableStakeSplit, // bool + StakingEfficiency, // double + MinStakeSplitValue, // int + OptionIDRowCount }; void Init(); diff --git a/src/util/system.h b/src/util/system.h index 7671c84649..66184086eb 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -108,6 +108,7 @@ class ArgsManager ALLOW_INT = 0x02, ALLOW_STRING = 0x04, ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING, + IMMEDIATE_EFFECT = 0x08, DEBUG_ONLY = 0x100, /* Some options would cause cross-contamination if values for * mainnet were used while running on regtest/testnet (or vice-versa). @@ -117,7 +118,7 @@ class ArgsManager NETWORK_ONLY = 0x200, // This argument's value is sensitive (such as a password). SENSITIVE = 0x400, - COMMAND = 0x800, + COMMAND = 0x800 }; protected: From 352716fbc4b0bc30478a42e43c91de8470db1f0f Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 6 Jun 2021 21:51:24 -0400 Subject: [PATCH 197/280] Implement listsettings This provides an rpc command to list all settings in rpc format. --- src/rpc/misc.cpp | 14 +++++++++ src/rpc/server.cpp | 1 + src/rpc/server.h | 1 + src/util/system.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++ src/util/system.h | 16 ++++++++-- 5 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 9e8164b0f6..36a92cf6b6 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -82,3 +82,17 @@ UniValue logging(const UniValue& params, bool fHelp) return result; } + +UniValue listsettings(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size()) + { + throw runtime_error( + "listsettings\n" + "Outputs all arguments/settings in JSON format.\n" + ); + } + + return gArgs.OutputArgs(); +} + diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 0a0b0f36b9..eedb940d2b 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -377,6 +377,7 @@ static const CRPCCommand vRPCCommands[] = { "listdata", &listdata, cat_developer }, { "listprojects", &listprojects, cat_developer }, { "listresearcheraccounts", &listresearcheraccounts, cat_developer }, + { "listsettings", &listsettings, cat_developer }, { "logging", &logging, cat_developer }, { "network", &network, cat_developer }, { "parseaccrualsnapshotfile",&parseaccrualsnapshotfile,cat_developer }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 761b22f713..3969d8a010 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -191,6 +191,7 @@ extern UniValue inspectaccrualsnapshot(const UniValue& params, bool fHelp); extern UniValue listdata(const UniValue& params, bool fHelp); extern UniValue listprojects(const UniValue& params, bool fHelp); extern UniValue listresearcheraccounts(const UniValue& params, bool fHelp); +extern UniValue listsettings(const UniValue& params, bool fHelp); extern UniValue logging(const UniValue& params, bool fHelp); extern UniValue network(const UniValue& params, bool fHelp); extern UniValue parseaccrualsnapshotfile(const UniValue& params, bool fHelp); diff --git a/src/util/system.cpp b/src/util/system.cpp index b1d35eb171..e1eb46fc2f 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1014,6 +1014,43 @@ void ArgsManager::logArgsPrefix( } } +UniValue ArgsManager::OutputArgsSection( + const std::string& section, + const std::map>& args) const +{ + UniValue result(UniValue::VOBJ); + UniValue settings(UniValue::VARR); + + std::string section_str = section.empty() ? "" : "[" + section + "] "; + for (const auto& arg : args) { + + UniValue setting(UniValue::VOBJ); + + for (const auto& value : arg.second) { + std::optional flags = GetArgFlags('-' + arg.first); + if (flags) { + if (*flags & SENSITIVE) + { + setting.pushKV(arg.first, "****"); + } + else + { + setting.pushKV(arg.first, value); + } + + setting.pushKV("changeable_without_restart", (*flags & IMMEDIATE_EFFECT) ? "true" : "false"); + + settings.push_back(setting); + } + } + } + + result.pushKV("section", section_str); + result.pushKV("settings", settings); + + return result; +} + void ArgsManager::LogArgs() const { LOCK(cs_args); @@ -1026,6 +1063,44 @@ void ArgsManager::LogArgs() const logArgsPrefix("Command-line arg:", "", m_settings.command_line_options); } +UniValue ArgsManager::OutputArgs() const +{ + UniValue result(UniValue::VOBJ); + UniValue sections(UniValue::VARR); + UniValue args_section(UniValue::VOBJ); + UniValue settings(UniValue::VARR); + + LOCK(cs_args); + + for (const auto& section : m_settings.ro_config) { + + args_section = OutputArgsSection(section.first, section.second); + sections.push_back(args_section); + } + result.pushKV("ro_config_file_args", sections); + + // There are no sections for rw_settings and the command line. + for (const auto& setting : m_settings.rw_settings) { + UniValue arg(UniValue::VOBJ); + std::optional flags = GetArgFlags('-' + setting.first); + + arg.pushKV(setting.first, setting.second); + + arg.pushKV("changeable_without_restart", (*flags & IMMEDIATE_EFFECT) ? "true" : "false"); + + settings.push_back(arg); + } + + result.pushKV("setting_file_args", settings); + + args_section.clear(); + args_section = OutputArgsSection("none", m_settings.command_line_options); + result.pushKV("command_line_args", find_value(args_section, "settings")); + + return result; +} + + // When we port the interfaces file over from Bitcoin, these two functions should be moved there. util::SettingsValue getRwSetting(const std::string& name) { diff --git a/src/util/system.h b/src/util/system.h index 66184086eb..213e1daa8c 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -378,12 +378,22 @@ class ArgsManager */ void LogArgs() const; + /** + * Output the settings as UniValue. + */ + UniValue OutputArgs() const; + private: // Helper function for LogArgs(). void logArgsPrefix( - const std::string& prefix, - const std::string& section, - const std::map>& args) const; + const std::string& prefix, + const std::string& section, + const std::map>& args) const; + + UniValue OutputArgsSection( + const std::string& section, + const std::map>& args) const; + }; extern ArgsManager gArgs; From 1988cfa1480c17b6df1dcc1b1e23877ffa44160a Mon Sep 17 00:00:00 2001 From: jamescowens Date: Mon, 7 Jun 2021 18:23:05 -0400 Subject: [PATCH 198/280] Implement changesettings --- src/rpc/misc.cpp | 137 ++++++++++++++++++++++++++++++++++++++++++++ src/rpc/server.cpp | 1 + src/rpc/server.h | 1 + src/util/system.cpp | 6 +- 4 files changed, 141 insertions(+), 4 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 36a92cf6b6..42097c5fde 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -96,3 +96,140 @@ UniValue listsettings(const UniValue& params, bool fHelp) return gArgs.OutputArgs(); } +UniValue changesettings(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1) + { + throw runtime_error( + "changesettings [name=value] ... [name=value]\n" + "\n" + "name=value: name and value pair for setting to store/change (1st mandatory, 2nd+ optional).\n" + "\n" + "Note that the settings should be done in the same format as config file entries.\n" + "\n" + "Example:" + "changesettings enable enablestakesplit=1 stakingefficiency=98 minstakesplitvalue=800\n" + ); + } + + // -------- name ------------ value - value_changed - immediate_effect + std::map> valid_settings; + + UniValue result(UniValue::VOBJ); + UniValue settings_stored_with_no_state_change(UniValue::VARR); + UniValue settings_immediate(UniValue::VARR); + UniValue settings_applied_requiring_restart(UniValue::VARR); + //UniValue invalid_settings_ignored(UniValue::VARR); + + // Validation + for (unsigned int i = 0; i < params.size(); ++i) + { + std::string param = params[i].get_str(); + + if (param.size() > 0 && param[0] == '-') + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Incorrectly formatted setting change: " + param); + } + + std::string::size_type pos; + std::string name; + std::string value; + + if ((pos = param.find('=')) != std::string::npos) + { + name = param.substr(0, pos); + value = param.substr(pos + 1); + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Incorrectly formatted setting change: " + param); + } + + std::optional flags = gArgs.GetArgFlags('-' + name); + + if (!flags) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid setting: " + param); + } + + // TODO: Record explicit default state for settings. + // This currently has a problem that I am not sure yet how to solve. Settings that are defaulted to true, unless + // they are set to the contrary, such as -staking, will falsely indicate a change because the defaulted state is + // not explicitly stored for comparison. After there is an explicit entry defined in the settings file, it works + // correctly. + + // Also, the overloading of GetArg is NOT helpful here... + std::string current_value; + + // It is either a string or a number.... One of these will succeed. + try + { + current_value = gArgs.GetArg(name, "never_used_default"); + } + catch (std::exception& e) + { + // If it is a number convert back to a string. + current_value = ToString(gArgs.GetArg(name, 1)); + } + + bool value_changed = (current_value != value); + bool immediate_effect = *flags & ArgsManager::IMMEDIATE_EFFECT; + + auto insert_pair = valid_settings.insert(std::make_pair( + name, std::make_tuple(value, value_changed, immediate_effect))); + + if (!insert_pair.second) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "changesettings does not support more than one instance of the same " + "setting: " + param); + } + } + + // Now that validation is done do the update work. + bool restart_required = false; + + for (const auto& setting : valid_settings) + { + const std::string& name = setting.first; + const std::string& value = std::get<0>(setting.second); + const bool& value_changed = std::get<1>(setting.second); + const bool& immediate_effect = std::get<2>(setting.second); + + std::string param = name + "=" + value; + + // Regardless, store in r-w settings file. + if (!updateRwSetting(name, value)) + { + throw JSONRPCError(RPC_MISC_ERROR, "Error storing setting in read-write settings file."); + } + + if (value_changed) + { + gArgs.ForceSetArg(name, value); + + if (immediate_effect) + { + settings_immediate.push_back(param); + } + else + { + settings_applied_requiring_restart.push_back(param); + + // Record if restart required. + restart_required |= !immediate_effect; + } + } + else + { + settings_stored_with_no_state_change.push_back(param); + } + } + + result.pushKV("settings_change_requires_restart", restart_required); + result.pushKV("settings_stored_with_no_state_change", settings_stored_with_no_state_change); + result.pushKV("settings_changed_taking_immediate_effect", settings_immediate); + result.pushKV("settings_changed_requiring_restart", settings_applied_requiring_restart); + + return result; +} + diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index eedb940d2b..85a5701454 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -366,6 +366,7 @@ static const CRPCCommand vRPCCommands[] = { "auditsnapshotaccrual", &auditsnapshotaccrual, cat_developer }, { "auditsnapshotaccruals", &auditsnapshotaccruals, cat_developer }, { "addkey", &addkey, cat_developer }, + { "changesettings", &changesettings, cat_developer }, { "currentcontractaverage", ¤tcontractaverage, cat_developer }, { "debug", &debug, cat_developer }, { "dumpcontracts", &dumpcontracts, cat_developer }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 3969d8a010..df48464023 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -221,6 +221,7 @@ extern UniValue scraperreport(const UniValue& params, bool fHelp); // Network extern UniValue addnode(const UniValue& params, bool fHelp); extern UniValue askforoutstandingblocks(const UniValue& params, bool fHelp); +extern UniValue changesettings(const UniValue& params, bool fHelp); extern UniValue clearbanned(const UniValue& params, bool fHelp); extern UniValue currenttime(const UniValue& params, bool fHelp); extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp); diff --git a/src/util/system.cpp b/src/util/system.cpp index e1eb46fc2f..24494c12ad 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1038,7 +1038,7 @@ UniValue ArgsManager::OutputArgsSection( setting.pushKV(arg.first, value); } - setting.pushKV("changeable_without_restart", (*flags & IMMEDIATE_EFFECT) ? "true" : "false"); + setting.pushKV("changeable_without_restart", (bool)(*flags & IMMEDIATE_EFFECT)); settings.push_back(setting); } @@ -1086,7 +1086,7 @@ UniValue ArgsManager::OutputArgs() const arg.pushKV(setting.first, setting.second); - arg.pushKV("changeable_without_restart", (*flags & IMMEDIATE_EFFECT) ? "true" : "false"); + arg.pushKV("changeable_without_restart", (bool)(*flags & IMMEDIATE_EFFECT)); settings.push_back(arg); } @@ -1263,5 +1263,3 @@ std::pair WinCmdLineArgs::get() } #endif } // namespace util - - From 14a5646e5018dce876795b5cd07f24402456cb6f Mon Sep 17 00:00:00 2001 From: jamescowens Date: Wed, 9 Jun 2021 09:57:07 -0400 Subject: [PATCH 199/280] Implement dynamic sidestaking entries This commit introduces two new settings for sidestaking, -sidestakeaddresses=address1,address2,..., addressN -sidestakeallocations=alloc1,alloc2,...,allocN These may be used in both the config file and with changesettings as an alternative to the original sidestake=address,allocation entries. If the new entries are present, are not empty, and are correctly filled out then they will take precedence over the older entries. The reason this is necessary is that the read-write settings file does not support multiple instances of the same setting, so a restructure was required to support the r-w override. --- src/gridcoin/staking/status.cpp | 2 +- src/gridcoin/staking/status.h | 2 +- src/init.cpp | 17 +++- src/miner.cpp | 171 ++++++++++++++++++-------------- src/miner.h | 2 +- src/qt/bitcoingui.cpp | 2 +- src/rpc/mining.cpp | 5 +- src/rpc/misc.cpp | 3 +- 8 files changed, 121 insertions(+), 83 deletions(-) diff --git a/src/gridcoin/staking/status.cpp b/src/gridcoin/staking/status.cpp index 5f1f718545..d2d834f9c0 100644 --- a/src/gridcoin/staking/status.cpp +++ b/src/gridcoin/staking/status.cpp @@ -18,7 +18,7 @@ namespace { //! constexpr const char* STAKING_ERROR_STRINGS[] { "None", - "Commanded disabled", + "Disabled by configuration", "No Mature Coins", "No coins", "Entire balance reserved", diff --git a/src/gridcoin/staking/status.h b/src/gridcoin/staking/status.h index 1c2728d1c6..e921e53304 100644 --- a/src/gridcoin/staking/status.h +++ b/src/gridcoin/staking/status.h @@ -20,7 +20,7 @@ class MinerStatus enum ReasonNotStakingCategory { NONE, - COMMANDED_DISABLED, + DISABLED_BY_CONFIGURATION, NO_MATURE_COINS, NO_COINS, ENTIRE_BALANCE_RESERVED, diff --git a/src/init.cpp b/src/init.cpp index 7c3e53aea6..c1b19a128b 100755 --- a/src/init.cpp +++ b/src/init.cpp @@ -363,8 +363,21 @@ void SetupServerArgs() argsman.AddArg("-sidestake=", "Sidestake destination and allocation entry. There can be as many " "specified as desired. Only six per stake can be sent. If more than " "six are specified. Six are randomly chosen for each stake. Only active " - "if -enablesidestaking is set.", - ArgsManager::ALLOW_ANY, OptionsCategory::STAKING); + "if -enablesidestaking is set. These settings are overridden if " + "-sidestakeaddresses and -stakestakeallocations are set.", + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); + argsman.AddArg("-sidestakeaddresses=", "Sidestake destination entry. There can be as many " + "specified as desired. Only six per stake can be sent. If more than " + "six are specified. Six are randomly chosen for each stake. Only active " + "if -enablesidestaking is set. If set along with -sidestakeallocations " + "overrides the -sidestake entries.", + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); + argsman.AddArg("-sidestakeallocations=percent1,percent2,...,percentN>", "Sidestake allocation entry. There can be as many " + "specified as desired. Only six per stake can be sent. If more than " + "six are specified. Six are randomly chosen for each stake. Only active " + "if -enablesidestaking is set. If set along with -sidestakeaddresses " + "overrides the -sidestake entries.", + ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); argsman.AddArg("-enablestakesplit", "Enable unspent output spitting when staking to optimize staking efficiency " "(default: 0", ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING); diff --git a/src/miner.cpp b/src/miner.cpp index 443b914397..19b9019e0d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1180,7 +1180,7 @@ bool IsMiningAllowed(CWallet *pwallet) if (!gArgs.GetBoolArg("-staking", true)) { LOCK(g_miner_status.lock); - g_miner_status.SetReasonNotStaking(GRC::MinerStatus::COMMANDED_DISABLED); + g_miner_status.SetReasonNotStaking(GRC::MinerStatus::DISABLED_BY_CONFIGURATION); status = false; } @@ -1189,87 +1189,108 @@ bool IsMiningAllowed(CWallet *pwallet) // This function parses the config file for the directives for side staking. It is used // in StakeMiner for the miner loop and also called by rpc getmininginfo. -bool GetSideStakingStatusAndAlloc(SideStakeAlloc& vSideStakeAlloc) +SideStakeAlloc GetSideStakingStatusAndAlloc() { - vector vSubParam; - std::string sAddress; - double dAllocation = 0.0; + SideStakeAlloc vSideStakeAlloc; + std::vector> raw_vSideStakeAlloc; double dSumAllocation = 0.0; - bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking"); - LogPrint(BCLog::LogFlags::MINER, "StakeMiner: fEnableSideStaking = %u", fEnableSideStaking); + // Parse destinations and allocations. We don't need to worry about any that are rejected other than a warning + // message, because any unallocated rewards will go back into the coinstake output(s). - // If side staking is enabled, parse destinations and allocations. We don't need to worry about any that are rejected - // other than a warning message, because any unallocated rewards will go back into the coinstake output(s). - if (fEnableSideStaking) + // If -sidestakeaddresses and -sidestakeallocations is set in either the config file or the r-w settings file + // and the settings are not empty and they are the same size, this will take precedence over the multiple entry + // -sidestake format. + std::vector addresses; + std::vector allocations; + + ParseString(gArgs.GetArg("-sidestakeaddresses", ""), ',', addresses); + ParseString(gArgs.GetArg("-sidestakeallocations", ""), ',', allocations); + + if (addresses.size() != allocations.size()) + { + LogPrintf("WARN: %s: Malformed new style sidestaking configuration entries. Reverting to original format.", + __func__); + } + + if (addresses.size() && addresses.size() == allocations.size()) + { + for (unsigned int i = 0; i < addresses.size(); ++i) + { + raw_vSideStakeAlloc.push_back(std::make_pair(addresses[i], allocations[i])); + } + } + else if (gArgs.GetArgs("-sidestake").size()) { - if (gArgs.GetArgs("-sidestake").size()) + for (auto const& sSubParam : gArgs.GetArgs("-sidestake")) { - for (auto const& sSubParam : gArgs.GetArgs("-sidestake")) + std::vector vSubParam; + + ParseString(sSubParam, ',', vSubParam); + if (vSubParam.size() != 2) { - ParseString(sSubParam, ',', vSubParam); - if (vSubParam.size() != 2) - { - LogPrintf("WARN: StakeMiner: Incompletely SideStake Allocation specified. Skipping SideStake entry."); - vSubParam.clear(); - continue; - } + LogPrintf("WARN: %s: Incomplete SideStake Allocation specified. Skipping SideStake entry.", __func__); + continue; + } - sAddress = vSubParam[0]; + raw_vSideStakeAlloc.push_back(std::make_pair(vSubParam[0], vSubParam[1])); + } + } - CBitcoinAddress address(sAddress); - if (!address.IsValid()) - { - LogPrintf("WARN: StakeMiner: ignoring sidestake invalid address %s.", sAddress.c_str()); - vSubParam.clear(); - continue; - } + for (auto const& entry : raw_vSideStakeAlloc) + { + std::string sAddress; + double dAllocation = 0.0; - try - { - dAllocation = stof(vSubParam[1]) / 100.0; - } - catch(...) - { - LogPrintf("WARN: StakeMiner: Invalid allocation provided. Skipping allocation."); - vSubParam.clear(); - continue; - } + sAddress = entry.first; - if (dAllocation <= 0) - { - LogPrintf("WARN: StakeMiner: Negative or zero allocation provided. Skipping allocation."); - vSubParam.clear(); - continue; - } + CBitcoinAddress address(sAddress); + if (!address.IsValid()) + { + LogPrintf("WARN: %s: ignoring sidestake invalid address %s.", __func__, sAddress); + continue; + } - // The below will stop allocations if someone has made a mistake and the total adds up to more than 100%. - // Note this same check is also done in SplitCoinStakeOutput, but it needs to be done here for two reasons: - // 1. Early alertment in the debug log, rather than when the first kernel is found, and 2. When the UI is - // hooked up, the SideStakeAlloc vector will be filled in by other than reading the config file and will - // skip the above code. - dSumAllocation += dAllocation; - if (dSumAllocation > 1.0) - { - LogPrintf("WARN: StakeMiner: allocation percentage over 100\%, ending sidestake allocations."); - break; - } + try + { + dAllocation = stof(entry.second) / 100.0; + } + catch (...) + { + LogPrintf("WARN: %s: Invalid allocation provided. Skipping allocation.", __func__); + continue; + } - vSideStakeAlloc.push_back(std::pair(sAddress, dAllocation)); - LogPrint(BCLog::LogFlags::MINER, "StakeMiner: SideStakeAlloc Address %s, Allocation %f", - sAddress.c_str(), dAllocation); + if (dAllocation <= 0) + { + LogPrintf("WARN: %s: Negative or zero allocation provided. Skipping allocation.", __func__); + continue; + } - vSubParam.clear(); - } + // The below will stop allocations if someone has made a mistake and the total adds up to more than 100%. + // Note this same check is also done in SplitCoinStakeOutput, but it needs to be done here for two reasons: + // 1. Early alertment in the debug log, rather than when the first kernel is found, and 2. When the UI is + // hooked up, the SideStakeAlloc vector will be filled in by other than reading the config file and will + // skip the above code. + dSumAllocation += dAllocation; + if (dSumAllocation > 1.0) + { + LogPrintf("WARN: %s: allocation percentage over 100\%, ending sidestake allocations.", __func__); + break; } - // If we get here and dSumAllocation is zero then the enablesidestaking flag was set, but no VALID distribution - // was provided in the config file, so warn in the debug log. - if (!dSumAllocation) - LogPrintf("WARN: StakeMiner: enablesidestaking was set in config but nothing has been allocated for" - " distribution!"); + + vSideStakeAlloc.push_back(std::pair(sAddress, dAllocation)); + LogPrint(BCLog::LogFlags::MINER, "INFO: %s: SideStakeAlloc Address %s, Allocation %f", + __func__, sAddress, dAllocation); } - return fEnableSideStaking; + // If we get here and dSumAllocation is zero then the enablesidestaking flag was set, but no VALID distribution + // was provided in the config file, so warn in the debug log. + if (!dSumAllocation) + LogPrintf("WARN: %s: enablesidestaking was set in config but nothing has been allocated for" + " distribution!"); + + return vSideStakeAlloc; } // This function parses the config file for the directives for stake splitting. It is used @@ -1325,17 +1346,21 @@ void StakeMiner(CWallet *pwallet) int64_t nMinStakeSplitValue = 0; double dEfficiency = 0; int64_t nDesiredStakeOutputValue = 0; - SideStakeAlloc vSideStakeAlloc = {}; - - // nMinStakeSplitValue and dEfficiency are out parameters. - bool fEnableStakeSplit = GetStakeSplitStatusAndParams(nMinStakeSplitValue, dEfficiency, nDesiredStakeOutputValue); - - // vSideStakeAlloc is an out parameter. - bool fEnableSideStaking = GetSideStakingStatusAndAlloc(vSideStakeAlloc); + SideStakeAlloc vSideStakeAlloc; while (!fShutdown) { - //wait for next round + // nMinStakeSplitValue and dEfficiency are out parameters. + bool fEnableStakeSplit = GetStakeSplitStatusAndParams(nMinStakeSplitValue, dEfficiency, nDesiredStakeOutputValue); + + bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking"); + + LogPrint(BCLog::LogFlags::MINER, "StakeMiner: fEnableSideStaking = %u", fEnableSideStaking); + + // vSideStakeAlloc is an out parameter. + if (fEnableSideStaking) vSideStakeAlloc = GetSideStakingStatusAndAlloc(); + + // wait for next round MilliSleep(nMinerSleep); g_timer.InitTimer("miner", LogInstance().WillLogCategory(BCLog::LogFlags::MISC)); diff --git a/src/miner.h b/src/miner.h index 343920bea5..e3b238382f 100644 --- a/src/miner.h +++ b/src/miner.h @@ -25,7 +25,7 @@ std::optional GetLastStake(CWallet& wallet); void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStakeSplit, bool &fEnableSideStaking, SideStakeAlloc &vSideStakeAlloc, double &dEfficiency); unsigned int GetNumberOfStakeOutputs(int64_t &nValue, int64_t &nMinStakeSplitValue, double &dEfficiency); -bool GetSideStakingStatusAndAlloc(SideStakeAlloc& vSideStakeAlloc); +SideStakeAlloc GetSideStakingStatusAndAlloc(); bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficiency, int64_t& nDesiredStakeOutputValue); #endif // NOVACOIN_MINER_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index fabbf9f362..f47653d3ff 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -705,7 +705,7 @@ void BitcoinGUI::createToolBars() } else { - labelStakingIcon->setToolTip(tr("Not staking: Commanded disabled.")); + labelStakingIcon->setToolTip(tr("Not staking: Disabled by configuration.")); } statusBar()->addPermanentWidget(frameBlocks); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 5deb0e517b..b29285e315 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -111,8 +111,9 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) // nMinStakeSplitValue, dEfficiency, and nDesiredStakeSplitValue are out parameters. bool fEnableStakeSplit = GetStakeSplitStatusAndParams(nMinStakeSplitValue, dEfficiency, nDesiredStakeSplitValue); - // vSideStakeAlloc is an out parameter. - bool fEnableSideStaking = GetSideStakingStatusAndAlloc(vSideStakeAlloc); + bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking"); + + vSideStakeAlloc = GetSideStakingStatusAndAlloc(); stakesplitting.pushKV("stake-splitting-enabled", fEnableStakeSplit); if (fEnableStakeSplit) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 42097c5fde..4519e3cbc2 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -166,7 +166,7 @@ UniValue changesettings(const UniValue& params, bool fHelp) { current_value = gArgs.GetArg(name, "never_used_default"); } - catch (std::exception& e) + catch (...) { // If it is a number convert back to a string. current_value = ToString(gArgs.GetArg(name, 1)); @@ -232,4 +232,3 @@ UniValue changesettings(const UniValue& params, bool fHelp) return result; } - From 5f521042e7d426f40bcc9a752c34e9802e59e9ef Mon Sep 17 00:00:00 2001 From: jamescowens Date: Sun, 13 Jun 2021 16:38:32 -0400 Subject: [PATCH 200/280] Increment version to 5.3.1.4 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 303cf1809d..80b326c37f 100755 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) define(_CLIENT_VERSION_MINOR, 3) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 3) +define(_CLIENT_VERSION_BUILD, 4) define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS,[The %s developers]) From c46e7fa08b5128add6b04f75dc46ac6977502daa Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Fri, 18 Jun 2021 00:17:42 +0300 Subject: [PATCH 201/280] Fix formatting --- src/miner.cpp | 5 -- src/net.cpp | 30 ++++++---- src/primitives/transaction.h | 6 +- src/qt/addressbookpage.cpp | 13 +++-- src/qt/addresstablemodel.cpp | 6 +- src/qt/askpassphrasedialog.cpp | 11 ++-- src/qt/bitcoingui.cpp | 23 ++++---- src/qt/clientmodel.cpp | 10 +++- src/qt/coincontroldialog.cpp | 13 +++-- src/qt/csvmodelwriter.cpp | 6 +- src/qt/editaddressdialog.cpp | 8 ++- src/qt/optionsdialog.cpp | 15 ++--- src/qt/qrcodedialog.cpp | 9 +-- src/qt/sendcoinsdialog.cpp | 11 ++-- src/qt/sendcoinsentry.cpp | 7 ++- src/qt/signverifymessagedialog.cpp | 7 ++- src/qt/trafficgraphwidget.cpp | 19 ++++--- src/qt/walletmodel.cpp | 19 +++++-- src/qt/walletmodel.h | 6 +- src/rpc/server.cpp | 10 ++-- src/test/getarg_tests.cpp | 3 +- src/test/gridcoin/cpid_tests.cpp | 22 ++------ src/wallet/db.h | 14 +++-- src/wallet/rpcwallet.cpp | 71 ++++++++++++++---------- src/wallet/wallet.cpp | 88 +++++++++++++++++------------- src/wallet/wallet.h | 22 +++++--- 26 files changed, 257 insertions(+), 197 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index 1685c43641..dddad22330 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -345,11 +345,6 @@ bool CreateRestOfTheBlock(CBlock &block, CBlockIndex* pindexPrev) { LogPrintf("ERROR: mempool transaction missing input"); - if (LogInstance().WillLogCategory(BCLog::LogFlags::VERBOSE)) - { - assert("mempool transaction missing input" == nullptr); - } - fMissingInputs = true; if (porphan) vOrphan.pop_back(); diff --git a/src/net.cpp b/src/net.cpp index e08562659c..ebffbcce99 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2003,41 +2003,47 @@ void StartNode(void* parg) // Start threads // - if (!gArgs.GetBoolArg("-dnsseed", true)) + if (!gArgs.GetBoolArg("-dnsseed", true)) { LogPrintf("DNS seeding disabled"); - else if (!netThreads->createThread(ThreadDNSAddressSeed, nullptr, "ThreadDNSAddressSeed")) + } else if (!netThreads->createThread(ThreadDNSAddressSeed, nullptr, "ThreadDNSAddressSeed")) { LogPrintf("Error: createThread(ThreadDNSAddressSeed) failed"); - + } // Map ports with UPnP - if (fUseUPnP) + if (fUseUPnP) { MapPort(); + } // Send and receive from sockets, accept connections - if (!netThreads->createThread(ThreadSocketHandler, nullptr, "ThreadSocketHandler")) + if (!netThreads->createThread(ThreadSocketHandler, nullptr, "ThreadSocketHandler")) { LogPrintf("Error: createThread(ThreadSocketHandler) failed"); + } // Initiate outbound connections from -addnode - if (!netThreads->createThread(ThreadOpenAddedConnections, nullptr, "ThreadOpenAddedConnections")) + if (!netThreads->createThread(ThreadOpenAddedConnections, nullptr, "ThreadOpenAddedConnections")) { LogPrintf("Error: createThread(ThreadOpenAddedConnections) failed"); + } // Initiate outbound connections - if (!netThreads->createThread(ThreadOpenConnections, nullptr, "ThreadOpenConnections")) + if (!netThreads->createThread(ThreadOpenConnections, nullptr, "ThreadOpenConnections")) { LogPrintf("Error: createThread(ThreadOpenConnections) failed"); + } // Process messages - if (!netThreads->createThread(ThreadMessageHandler, nullptr, "ThreadMessageHandler")) + if (!netThreads->createThread(ThreadMessageHandler, nullptr, "ThreadMessageHandler")) { LogPrintf("Error: createThread(ThreadMessageHandler) failed"); + } // Dump network addresses - if (!netThreads->createThread(ThreadDumpAddress, nullptr, "ThreadDumpAddress")) + if (!netThreads->createThread(ThreadDumpAddress, nullptr, "ThreadDumpAddress")) { LogPrintf("Error: createThread(ThreadDumpAddress) failed"); + } // Mine proof-of-stake blocks in the background - if (!gArgs.GetBoolArg("-staking", true)) + if (!gArgs.GetBoolArg("-staking", true)) { LogPrintf("Staking disabled"); - else - if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner")) + } else if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner")) { LogPrintf("Error: createThread(ThreadStakeMiner) failed"); + } } bool StopNode() diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 592760e72d..3ac7336134 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -20,11 +20,7 @@ class CInPoint CInPoint() { SetNull(); } CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } - void SetNull() - { - ptx = nullptr; - n = (unsigned int)-1; - } + void SetNull() { ptx = nullptr; n = (unsigned int)-1; } bool IsNull() const { return (ptx == nullptr && n == (unsigned int)-1); } }; diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 118dc23c79..12769fd8bd 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -17,12 +17,13 @@ #include "qrcodedialog.h" #endif -AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget* parent) : QDialog(parent), - ui(new Ui::AddressBookPage), - model(nullptr), - optionsModel(nullptr), - mode(mode), - tab(tab) +AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget* parent) + : QDialog(parent) + , ui(new Ui::AddressBookPage) + , model(nullptr) + , optionsModel(nullptr) + , mode(mode) + , tab(tab) { ui->setupUi(this); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 7f02c614df..faecf34e68 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -139,7 +139,11 @@ class AddressTablePriv } }; -AddressTableModel::AddressTableModel(CWallet* wallet, WalletModel* parent) : QAbstractTableModel(parent), walletModel(parent), wallet(wallet), priv(nullptr) +AddressTableModel::AddressTableModel(CWallet* wallet, WalletModel* parent) + : QAbstractTableModel(parent) + , walletModel(parent) + , wallet(wallet) + , priv(nullptr) { columns << tr("Label") << tr("Address"); priv = new AddressTablePriv(wallet, this); diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index ea8d9bbb83..8a972570da 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -10,11 +10,12 @@ extern bool fWalletUnlockStakingOnly; -AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget* parent) : QDialog(parent), - ui(new Ui::AskPassphraseDialog), - mode(mode), - model(nullptr), - fCapsLock(false) +AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget* parent) + : QDialog(parent) + , ui(new Ui::AskPassphraseDialog) + , mode(mode) + , model(nullptr) + , fCapsLock(false) { ui->setupUi(this); ui->oldPassphraseEdit->setMaxLength(MAX_PASSPHRASE_SIZE); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bed6b0b53d..71e6b7d4b8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -94,17 +94,18 @@ extern CWallet* pwalletMain; extern std::string FromQString(QString qs); extern CCriticalSection cs_ConvergedScraperStatsCache; -BitcoinGUI::BitcoinGUI(QWidget* parent) : QMainWindow(parent), - clientModel(nullptr), - walletModel(nullptr), - encryptWalletAction(nullptr), - changePassphraseAction(nullptr), - unlockWalletAction(nullptr), - lockWalletAction(nullptr), - trayIcon(nullptr), - notificator(nullptr), - rpcConsole(nullptr), - nWeight(0) +BitcoinGUI::BitcoinGUI(QWidget* parent) + : QMainWindow(parent) + , clientModel(nullptr) + , walletModel(nullptr) + , encryptWalletAction(nullptr) + , changePassphraseAction(nullptr) + , unlockWalletAction(nullptr) + , lockWalletAction(nullptr) + , trayIcon(nullptr) + , notificator(nullptr) + , rpcConsole(nullptr) + , nWeight(0) { QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 9e81a56b12..058c04e93f 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -21,8 +21,14 @@ static const int64_t nClientStartupTime = GetTime(); extern ConvergedScraperStats ConvergedScraperStatsCache; -ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) : QObject(parent), optionsModel(optionsModel), peerTableModel(nullptr), - banTableModel(nullptr), cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(nullptr) +ClientModel::ClientModel(OptionsModel* optionsModel, QObject* parent) + : QObject(parent) + , optionsModel(optionsModel) + , peerTableModel(nullptr) + , banTableModel(nullptr) + , cachedNumBlocks(0) + , cachedNumBlocksOfPeers(0) + , pollTimer(nullptr) { numBlocksAtStartup = -1; peerTableModel = new PeerTableModel(this); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b15a313857..5bf7711b75 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -26,12 +26,13 @@ using namespace std; -CoinControlDialog::CoinControlDialog(QWidget* parent, CCoinControl* coinControl, QList* payAmounts) : QDialog(parent), - m_inputSelectionLimit(GetMaxInputsForConsolidationTxn()), - ui(new Ui::CoinControlDialog), - coinControl(coinControl), - payAmounts(payAmounts), - model(nullptr) +CoinControlDialog::CoinControlDialog(QWidget* parent, CCoinControl* coinControl, QList* payAmounts) + : QDialog(parent) + , m_inputSelectionLimit(GetMaxInputsForConsolidationTxn()) + , ui(new Ui::CoinControlDialog) + , coinControl(coinControl) + , payAmounts(payAmounts) + , model(nullptr) { assert(coinControl != nullptr && payAmounts != nullptr); diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index c0acd474ea..04cc9f0464 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -4,8 +4,10 @@ #include #include -CSVModelWriter::CSVModelWriter(const QString& filename, QObject* parent) : QObject(parent), - filename(filename), model(nullptr) +CSVModelWriter::CSVModelWriter(const QString& filename, QObject* parent) + : QObject(parent) + , filename(filename) + , model(nullptr) { } diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 4d655d2e5a..6c8d9278b6 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -6,8 +6,12 @@ #include #include -EditAddressDialog::EditAddressDialog(Mode mode, QWidget* parent) : QDialog(parent), - ui(new Ui::EditAddressDialog), mapper(nullptr), mode(mode), model(nullptr) +EditAddressDialog::EditAddressDialog(Mode mode, QWidget* parent) + : QDialog(parent) + , ui(new Ui::EditAddressDialog) + , mapper(nullptr) + , mode(mode) + , model(nullptr) { ui->setupUi(this); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d2ddcfc8ef..42ed831c28 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -14,13 +14,14 @@ #include #include -OptionsDialog::OptionsDialog(QWidget* parent) : QDialog(parent), - ui(new Ui::OptionsDialog), - model(nullptr), - mapper(nullptr), - fRestartWarningDisplayed_Proxy(false), - fRestartWarningDisplayed_Lang(false), - fProxyIpValid(true) +OptionsDialog::OptionsDialog(QWidget* parent) + : QDialog(parent) + , ui(new Ui::OptionsDialog) + , model(nullptr) + , mapper(nullptr) + , fRestartWarningDisplayed_Proxy(false) + , fRestartWarningDisplayed_Lang(false) + , fProxyIpValid(true) { ui->setupUi(this); diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp index 00de650141..2982a982c3 100644 --- a/src/qt/qrcodedialog.cpp +++ b/src/qt/qrcodedialog.cpp @@ -11,10 +11,11 @@ #include -QRCodeDialog::QRCodeDialog(const QString& addr, const QString& label, bool enableReq, QWidget* parent) : QDialog(parent), - ui(new Ui::QRCodeDialog), - model(nullptr), - address(addr) +QRCodeDialog::QRCodeDialog(const QString& addr, const QString& label, bool enableReq, QWidget* parent) + : QDialog(parent) + , ui(new Ui::QRCodeDialog) + , model(nullptr) + , address(addr) { ui->setupUi(this); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 3b90368ae5..2ec584f34c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -24,11 +24,12 @@ #include #include -SendCoinsDialog::SendCoinsDialog(QWidget* parent) : QDialog(parent), - ui(new Ui::SendCoinsDialog), - coinControl(new CCoinControl), - payAmounts(new QList), - model(nullptr) +SendCoinsDialog::SendCoinsDialog(QWidget* parent) + : QDialog(parent) + , ui(new Ui::SendCoinsDialog) + , coinControl(new CCoinControl) + , payAmounts(new QList) + , model(nullptr) { ui->setupUi(this); diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 8c745478c2..b947958a94 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -10,9 +10,10 @@ #include #include -SendCoinsEntry::SendCoinsEntry(QWidget* parent) : QFrame(parent), - ui(new Ui::SendCoinsEntry), - model(nullptr) +SendCoinsEntry::SendCoinsEntry(QWidget* parent) + : QFrame(parent) + , ui(new Ui::SendCoinsEntry) + , model(nullptr) { ui->setupUi(this); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 27521d970f..c7a6097429 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -16,9 +16,10 @@ #include -SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget* parent) : QDialog(parent), - ui(new Ui::SignVerifyMessageDialog), - model(nullptr) +SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget* parent) + : QDialog(parent) + , ui(new Ui::SignVerifyMessageDialog) + , model(nullptr) { ui->setupUi(this); diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 1798419e92..b6c3e0be06 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -14,15 +14,16 @@ #define XMARGIN 10 #define YMARGIN 10 -TrafficGraphWidget::TrafficGraphWidget(QWidget* parent) : QWidget(parent), - timer(nullptr), - fMax(0.0f), - nMins(0), - vSamplesIn(), - vSamplesOut(), - nLastBytesIn(0), - nLastBytesOut(0), - clientModel(nullptr) +TrafficGraphWidget::TrafficGraphWidget(QWidget* parent) + : QWidget(parent) + , timer(nullptr) + , fMax(0.0f) + , nMins(0) + , vSamplesIn() + , vSamplesOut() + , nLastBytesIn(0) + , nLastBytesOut(0) + , clientModel(nullptr) { timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(updateRates())); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 01d55297b9..d6ddc30a84 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -15,12 +15,19 @@ void qtInsertConfirm(double dAmt, std::string sFrom, std::string sTo, std::string txid); -WalletModel::WalletModel(CWallet* wallet, OptionsModel* optionsModel, QObject* parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(nullptr), - transactionTableModel(nullptr), - cachedBalance(0), cachedStake(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), - cachedNumTransactions(0), - cachedEncryptionStatus(Unencrypted), - cachedNumBlocks(0) +WalletModel::WalletModel(CWallet* wallet, OptionsModel* optionsModel, QObject* parent) + : QObject(parent) + , wallet(wallet) + , optionsModel(optionsModel) + , addressTableModel(nullptr) + , transactionTableModel(nullptr) + , cachedBalance(0) + , cachedStake(0) + , cachedUnconfirmedBalance(0) + , cachedImmatureBalance(0) + , cachedNumTransactions(0) + , cachedEncryptionStatus(Unencrypted) + , cachedNumBlocks(0) { addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 248f8386b0..03be37d084 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -91,10 +91,10 @@ class WalletModel : public QObject SendCoinsReturn sendCoins(const QList& recipients, const CCoinControl* coinControl = nullptr); // Wallet encryption - bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); + bool setWalletEncrypted(bool encrypted, const SecureString& passphrase); // Passphrase only needed when unlocking - bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); - bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); + bool setWalletLocked(bool locked, const SecureString& passPhrase=SecureString()); + bool changePassphrase(const SecureString& oldPass, const SecureString& newPass); // RAI object for unlocking wallet, returned by requestUnlock() class UnlockContext diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 0155e7299a..0539d7ef43 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -462,11 +462,12 @@ CRPCTable::CRPCTable() } } -const CRPCCommand *CRPCTable::operator[](string name) const +const CRPCCommand* CRPCTable::operator[](string name) const { - map::const_iterator it = mapCommands.find(name); - if (it == mapCommands.end()) + auto it = mapCommands.find(name); + if (it == mapCommands.end()) { return nullptr; + } return it->second; } @@ -703,8 +704,9 @@ void StopRPCThreads() } rpc_io_service->stop(); - if (rpc_worker_group != nullptr) + if (rpc_worker_group != nullptr) { rpc_worker_group->join_all(); + } delete rpc_worker_group; rpc_worker_group = nullptr; diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 30fdcfd16c..bcf7ccd636 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -32,8 +32,9 @@ static bool ResetArgs(const std::string& strAddArg, const std::string& strArgIn // Convert to char*: std::vector vecChar; - for (std::string& s : vecArg) + for (std::string& s : vecArg) { vecChar.push_back(s.c_str()); + } std::string error; diff --git a/src/test/gridcoin/cpid_tests.cpp b/src/test/gridcoin/cpid_tests.cpp index ceaac8fb02..4116a74b40 100644 --- a/src/test/gridcoin/cpid_tests.cpp +++ b/src/test/gridcoin/cpid_tests.cpp @@ -318,24 +318,10 @@ BOOST_AUTO_TEST_CASE(it_parses_a_cpid_mining_id) if (const GRC::CpidOption cpid = mining_id.TryCpid()) { BOOST_CHECK(*cpid == expected); - BOOST_CHECK(cpid->Raw() == (std::array{ - 0x00, - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - })); + BOOST_CHECK(cpid->Raw() == (std::array { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + })); } else { BOOST_FAIL("MiningId variant does not contain the CPID."); } diff --git a/src/wallet/db.h b/src/wallet/db.h index 6f519337b9..51a60bc5e8 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -128,8 +128,9 @@ class CDB datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(activeTxn, &datKey, &datValue, 0); memset(datKey.get_data(), 0, datKey.get_size()); - if (datValue.get_data() == nullptr) + if (datValue.get_data() == nullptr) { return false; + } // Unserialize value try { @@ -219,12 +220,14 @@ class CDB Dbc* GetCursor() { - if (!pdb) + if (!pdb) { return nullptr; + } Dbc* pcursor = nullptr; int ret = pdb->cursor(nullptr, &pcursor, 0); - if (ret != 0) + if (ret != 0) { return nullptr; + } return pcursor; } @@ -246,10 +249,11 @@ class CDB datKey.set_flags(DB_DBT_MALLOC); datValue.set_flags(DB_DBT_MALLOC); int ret = pcursor->get(&datKey, &datValue, fFlags); - if (ret != 0) + if (ret != 0) { return ret; - else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) + } else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) { return 99999; + } // Convert to streams ssKey.SetType(SER_DISK); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 380ab13adb..853f0cbb3d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -339,8 +339,9 @@ UniValue getaccount(const UniValue& params, bool fHelp) string strAccount; map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); - if (mi != pwalletMain->mapAddressBook.end() && !mi->second.empty()) + if (mi != pwalletMain->mapAddressBook.end() && !mi->second.empty()) { strAccount = mi->second; + } return strAccount; } @@ -554,13 +555,17 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = it->second; - if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) { continue; + } - for (auto const& txout : wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) + for (auto const& txout : wtx.vout) { + if (txout.scriptPubKey == scriptPubKey) { + if (wtx.GetDepthInMainChain() >= nMinDepth) { nAmount += txout.nValue; + } + } + } } return ValueFromAmount(nAmount); @@ -605,15 +610,18 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = it->second; - if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx)) { continue; + } for (auto const& txout : wtx.vout) { CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address) && (IsMine(*pwalletMain, address) != ISMINE_NO) && setAddress.count(address)) - if (wtx.GetDepthInMainChain() >= nMinDepth) + if (ExtractDestination(txout.scriptPubKey, address) && (IsMine(*pwalletMain, address) != ISMINE_NO) && setAddress.count(address)) { + if (wtx.GetDepthInMainChain() >= nMinDepth) { nAmount += txout.nValue; + } + } } } @@ -628,8 +636,9 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = it->second; - if (!IsFinalTx(wtx) || wtx.GetDepthInMainChain() < 0) + if (!IsFinalTx(wtx) || wtx.GetDepthInMainChain() < 0) { continue; + } int64_t nReceived, nSent, nFee; wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); @@ -1040,18 +1049,17 @@ UniValue sendmany(const UniValue& params, bool fHelp) // Check funds & Support non-account sendmany int64_t nBalance = 0; - if (bFromAccount) + if (bFromAccount) { nBalance = GetAccountBalance(strAccount, nMinDepth); - - else - { + } else { isminefilter filter = ISMINE_SPENDABLE; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = it->second; - if (!wtx.IsTrusted()) + if (!wtx.IsTrusted()) { continue; + } int64_t allFee; string strSentAccount; @@ -1060,11 +1068,13 @@ UniValue sendmany(const UniValue& params, bool fHelp) wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (wtx.GetDepthInMainChain() >= nMinDepth && wtx.GetBlocksToMaturity() == 0) { - for (auto const& r : listReceived) + for (auto const& r : listReceived) { nBalance += r.amount; + } } - for (auto const& r : listSent) + for (auto const& r : listSent) { nBalance -= r.amount; + } nBalance -= allFee; } } @@ -1291,8 +1301,9 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) else { UniValue obj(UniValue::VOBJ); - if(fIsWatchonly) - obj.pushKV("involvesWatchonly", true); + if (fIsWatchonly) { + obj.pushKV("involvesWatchonly", true); + } obj.pushKV("address", address.ToString()); obj.pushKV("account", strAccount); obj.pushKV("amount", ValueFromAmount(nAmount)); @@ -1304,7 +1315,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) for (const uint256& _item : it->second.txids) { transactions.push_back(_item.GetHex()); } - } + } obj.pushKV("txids", transactions); ret.push_back(obj); } @@ -1312,15 +1323,15 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) if (fByAccounts) { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + for (const auto& it : mapAccountTally) { - int64_t nAmount = it->second.nAmount; - int nConf = it->second.nConf; + int64_t nAmount = it.second.nAmount; + int nConf = it.second.nConf; UniValue obj(UniValue::VOBJ); - if (it->second.fIsWatchonly) + if (it.second.fIsWatchonly) obj.pushKV("involvesWatchonly", true); - obj.pushKV("account", it->first); - obj.pushKV("amount", ValueFromAmount(nAmount)); + obj.pushKV("account", it.first); + obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); ret.push_back(obj); } @@ -1626,13 +1637,17 @@ UniValue listtransactions(const UniValue& params, bool fHelp) for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx* const pwtx = it->second.first; - if (pwtx != nullptr) + if (pwtx != nullptr) { ListTransactions(*pwtx, strAccount, 0, true, ret, filter); + } CAccountingEntry* const pacentry = it->second.second; - if (pacentry != nullptr) + if (pacentry != nullptr) { AcentryToJSON(*pacentry, strAccount, ret); + } - if ((int)ret.size() >= (nCount+nFrom)) break; + if ((int)ret.size() >= (nCount + nFrom)) { + break; + } } // ret is newest to oldest diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0a1def3df1..31279fe772 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -428,14 +428,13 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock, CWalletDB* LOCK(cs_wallet); for (auto const& txin : tx.vin) { - map::iterator mi = mapWallet.find(txin.prevout.hash); + auto mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { CWalletTx& wtx = mi->second; - if (txin.prevout.n >= wtx.vout.size()) + if (txin.prevout.n >= wtx.vout.size()) { LogPrintf("WalletUpdateSpent: bad wtx %s", wtx.GetHash().ToString()); - else if (!wtx.IsSpent(txin.prevout.n) && (IsMine(wtx.vout[txin.prevout.n]) != ISMINE_NO)) - { + } else if (!wtx.IsSpent(txin.prevout.n) && (IsMine(wtx.vout[txin.prevout.n]) != ISMINE_NO)) { LogPrint(BCLog::LogFlags::VERBOSE, "WalletUpdateSpent found spent coin %s gC %s", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(pwalletdb); @@ -447,7 +446,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock, CWalletDB* if (fBlock) { uint256 hash = tx.GetHash(); - map::iterator mi = mapWallet.find(hash); + auto mi = mapWallet.find(hash); CWalletTx& wtx = mi->second; for (auto const& txout : tx.vout) @@ -614,12 +613,13 @@ isminetype CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); - map::const_iterator mi = mapWallet.find(txin.prevout.hash); + const auto mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = mi->second; - if (txin.prevout.n < prev.vout.size()) + if (txin.prevout.n < prev.vout.size()) { return IsMine(prev.vout[txin.prevout.n]); + } } } return ISMINE_NO; @@ -678,15 +678,16 @@ int CWalletTx::GetRequestCount() const // Generated block if (!hashBlock.IsNull()) { - map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) + const auto mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) { nRequests = mi->second; + } } } else { // Did anyone request this transaction? - map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + const auto mi = pwallet->mapRequestCount.find(GetHash()); if (mi != pwallet->mapRequestCount.end()) { nRequests = mi->second; @@ -694,11 +695,12 @@ int CWalletTx::GetRequestCount() const // How about the block it's in? if (nRequests == 0 && !hashBlock.IsNull()) { - map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) + const auto mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) { nRequests = mi->second; - else + } else { nRequests = 1; // If it's in someone else's block it must have got out + } } } } @@ -918,9 +920,10 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, { if (pwallet->mapAddressBook.count(r.destination)) { - map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); - if (mi != pwallet->mapAddressBook.end() && mi->second == strAccount) + const auto mi = pwallet->mapAddressBook.find(r.destination); + if (mi != pwallet->mapAddressBook.end() && mi->second == strAccount) { nReceived += r.amount; + } } else if (strAccount.empty()) { @@ -938,8 +941,9 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) if (SetMerkleBranch() < COPY_DEPTH) { vector vWorkQueue; - for (auto const& txin : vin) + for (auto const& txin : vin) { vWorkQueue.push_back(txin.prevout.hash); + } // This critsect is OK because txdb is already open { @@ -1230,8 +1234,9 @@ int64_t CWallet::GetUnconfirmedBalance() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &it->second; - if (!IsFinalTx(*pcoin) || (!pcoin->IsConfirmed() && !pcoin->fFromMe && pcoin->IsInMainChain())) + if (!IsFinalTx(*pcoin) || (!pcoin->IsConfirmed() && !pcoin->fFromMe && pcoin->IsInMainChain())) { nTotal += pcoin->GetAvailableCredit(); + } } } return nTotal; @@ -1245,8 +1250,9 @@ int64_t CWallet::GetImmatureBalance() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx& pcoin = it->second; - if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain()) + if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain()) { nTotal += GetCredit(pcoin); + } } } return nTotal; @@ -1265,25 +1271,28 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const int nDepth = pcoin->GetDepthInMainChain(); if (!fIncludeStakedCoins) { - if (!IsFinalTx(*pcoin)) + if (!IsFinalTx(*pcoin)) { continue; + } - if (fOnlyConfirmed && !pcoin->IsTrusted()) + if (fOnlyConfirmed && !pcoin->IsTrusted()) { continue; + } - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) { continue; + } - if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) + if (nDepth < 0) { continue; - - if (nDepth < 0) + } + } + else + { + if (nDepth < 1) { continue; - } - else - { - if (nDepth < 1) continue; - } + } + } for (unsigned int i = 0; i < pcoin->vout.size(); i++) { @@ -1292,8 +1301,7 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const (fIncludeStakedCoins && pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)) { vCoins.push_back(COutput(pcoin, i, nDepth)); } - } - + } } } } @@ -1451,8 +1459,9 @@ int64_t CWallet::GetStake() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &it->second; - if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) { nTotal += CWallet::GetCredit(*pcoin); + } } return nTotal; } @@ -1464,8 +1473,9 @@ int64_t CWallet::GetNewMint() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &it->second; - if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) { nTotal += CWallet::GetCredit(*pcoin); + } } return nTotal; } @@ -1543,8 +1553,9 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, if (nTotalLower < nTargetValue) { - if (coinLowestLarger.second.first == nullptr) + if (coinLowestLarger.second.first == nullptr) { return false; + } setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; return true; @@ -2594,8 +2605,9 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64_t& nBalanceInQuestion, bo LOCK(cs_wallet); vector vCoins; vCoins.reserve(mapWallet.size()); - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { vCoins.push_back(&it->second); + } CWalletDB walletdb(strWalletFile); @@ -2604,8 +2616,9 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64_t& nBalanceInQuestion, bo { // Find the corresponding transaction index CTxIndex txindex; - if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) + if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) { continue; + } for (unsigned int n=0; n < pcoin->vout.size(); n++) { if ((IsMine(pcoin->vout[n]) != ISMINE_NO) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) @@ -2877,8 +2890,9 @@ MinedType GetGeneratedType(const CWallet *wallet, const uint256& tx, unsigned in BlockMap::iterator mi = mapBlockIndex.find(hashblock); - if (mi == mapBlockIndex.end()) + if (mi == mapBlockIndex.end()) { return MinedType::UNKNOWN; + } CBlockIndex* blkindex = mi->second; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 30dd197a03..c1c402d635 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -81,7 +81,9 @@ class CKeyPool class CWallet : public CCryptoKeyStore { private: - bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set>& setCoinsRet, int64_t& nValueRet, const CCoinControl* coinControl = nullptr, bool contract = false) const; + bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, + std::set>& setCoinsRet, int64_t& nValueRet, + const CCoinControl* coinControl = nullptr, bool contract = false) const; CWalletDB *pwalletdbEncryption; @@ -191,9 +193,12 @@ class CWallet : public CCryptoKeyStore void AvailableCoinsForStaking(std::vector& vCoins, unsigned int nSpendTime, int64_t& nBalanceOut) const; bool SelectCoinsForStaking(unsigned int nSpendTime, std::vector >& vCoinsRet, GRC::MinerStatus::ReasonNotStakingCategory& not_staking_error, int64_t& balance_out, bool fMiner = false) const; - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl* coinControl = nullptr, bool fIncludeStakingCoins = false) const; - bool SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; - bool SelectSmallestCoins(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl* coinControl = nullptr, + bool fIncludeStakingCoins = false) const; + bool SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, + std::set >& setCoinsRet, int64_t& nValueRet) const; + bool SelectSmallestCoins(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, + std::set >& setCoinsRet, int64_t& nValueRet) const; // keystore implementation // Generate a new key @@ -248,9 +253,12 @@ class CWallet : public CCryptoKeyStore int64_t GetImmatureBalance() const; int64_t GetStake() const; int64_t GetNewMint() const; - bool CreateTransaction(const std::vector>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); - bool CreateTransaction(const std::vector>& vecSend, std::set>& setCoins, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); - bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); + bool CreateTransaction(const std::vector>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, + int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); + bool CreateTransaction(const std::vector>& vecSend, std::set>& setCoins, + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr); + bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, + const CCoinControl* coinControl = nullptr); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); From e329407b2dff009cb119757d20ae387014ffebdb Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 19 Jun 2021 17:28:43 +0300 Subject: [PATCH 202/280] util: optimize HexStr --- src/util/strencodings.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 9b9ac6fa58..f20e3b07db 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -122,16 +123,19 @@ constexpr inline bool IsSpace(char c) noexcept { template std::string HexStr(const T itbegin, const T itend) { - std::string rv; + std::string rv(std::distance(itbegin, itend) * 2, '\0'); + static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - rv.reserve(std::distance(itbegin, itend) * 2); - for(T it = itbegin; it < itend; ++it) + + auto rvit = rv.begin(); + for (T it = itbegin; it < itend; ++it) { unsigned char val = (unsigned char)(*it); - rv.push_back(hexmap[val>>4]); - rv.push_back(hexmap[val&15]); + *rvit++ = hexmap[val >> 4]; + *rvit++ = hexmap[val & 15]; } + assert(rvit == rv.end()); return rv; } From 41a3b74c13ac2ced79119f7145262c5953564570 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Mon, 17 May 2021 10:12:39 -0700 Subject: [PATCH 203/280] Refactor, change explanation, go in more depth and use bytes instead since hex is more confusing --- src/rpc/client.cpp | 2 - src/rpc/rawtransaction.cpp | 472 ++++++++++++++++++++++--------------- 2 files changed, 280 insertions(+), 194 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 3c893a386c..2dea2592be 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -110,8 +110,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "consolidatemsunspent" , 2 }, { "consolidatemsunspent" , 3 }, { "consolidatemsunspent" , 4 }, - { "consolidatemsunspent" , 5 }, - { "consolidatemsunspent" , 6 }, { "consolidateunspent" , 3 }, { "consolidateunspent" , 4 }, { "getbalance" , 1 }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index f91e956062..f1f2a94d12 100755 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -24,6 +24,7 @@ #include "validation.h" #include "wallet/coincontrol.h" #include "wallet/wallet.h" +#include #include @@ -814,42 +815,148 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) return result; } -// MultiSig Tools +/* MultiSig Tool for consolidating multisig transactions + * + * In order to do the best possible calculation and prediction of end result you need to understand transaction serialization. + * It was brought to my attention that no one could understand where the numbers actually came from. With multisig transactions + * there is many simularities to standard transactions. Here i'll break down all aspects of a serialized hex transaction. + * I plan to break it down into sections for easier understanding since its quite detailed. Some details I'm unaware of thou. + * It is important to note that padding at new areas start with OP_0 aka 0x00 and padding at end of an area ends with OP_INVALIDOPCODE 0xff. + * Last information to know is that I will use byte size in calculations but explain in hex sizes here. Byte size is typically Hex size / 2. + * + * Alot of the space in the transaction is used up by signatures and redeemscript. I'll show the layout of both for the curious. + * + * Canonical Standard Per Signature: + * 47 30 44 02 20 (..) 02 20 (..) 01 + * + * + * sl: Canonical signature data length (typically 71-73 bytes in length as the ECDSA is 32 bytes and in some cases it can be longer) + * ch: 0x30 hex signifies Canonical signature. + * tl: Total length in hex of combined data padding, R & S data. + * dp: Padding of 0x02 between data points. + * rl: R length in hex. R is used for Public Key Recovery. + * rd: R Data; Typically 32 bytes in length. + * sl: S length in hex. S is signature + * sd: S Data; Typically 32 bytes in length. + * st: Signature type. + * + * Note: I'm not going into excessive details of the canonical signatures as there are many references to explain more indepth then needed for this function. + * We will say that the base size of a single signature is 1 + 73 + * After the signatures is 2 OP codes (2 bytes) for a multisignature tx. + * + * Base Signatures formula simplified: BSS = (74 * number_signatures_required) + 2 + * + * Redeemscript: + * 52 21 (..) 21 (..) 21 (..)... 53 ae + * ... + * + * sr: Signatures required in OP code + * pl: Pubkey length + * pk: Pubkey + * ts: Total signable pubkeys in OP code + * cm: OP code OP_CHECKMULTISIG + * + * The wallet/user will provide us with the redeemscript so we will know the size of the script in bytes and we add 4 bytes padding in the tx. + * + * Standard transaction begin with: + * 02 000000 ######## 01 + * <#ofvins> + * + * The start of a transaction contains the tx version of the transaction followed by 3 bytes of OP_0 which is padding. + * MISCDATA then is placed (likely the transaction time etc) followed by the amount of inputs in the transaction. + * We can count on the size to be unchanged since no other data will be placed in that area. + * + * The start of the transaction will be 9 bytes. + * + * Vin data in a transaction is stored as the following: + * (....) 02 000000 fc00 (.......)(................)ffffffff + * + * + * txid: the txid on the input being used + * vout#: the vout on the txid + * padding: 3 bytes of 0x00 at beginning of vin and 4 bytes of 0xff to signify sequence at end of each vin ( + * MISCDATA: Unknown but consitant across the board. + * sigdata: Signature data from canonical signature as mentioned above. + * redeemscript: redeemscript of the multisignature address so the wallet/network knows which addresses are involved in multisignature address. + * + * From this we can gather that the vin size will be 32 + 1 + 3 + 2 + BSS + RSS + 4. + * Or simplified to total vin size of all vins: TVIS = (38 + BSS + RSS + 4) * number_inputs + * + * Vout data in transaction is simply put since it is returning to the same address. + * 1 (...10...) 000000 17 a9 14 (......) 87 0000000000 + * <#ofvouts>