/ src / qt / bitcoin.cpp
bitcoin.cpp
  1  // Copyright (c) 2011-2022 The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #if defined(HAVE_CONFIG_H)
  6  #include <config/bitcoin-config.h>
  7  #endif
  8  
  9  #include <qt/bitcoin.h>
 10  
 11  #include <chainparams.h>
 12  #include <common/args.h>
 13  #include <common/init.h>
 14  #include <common/system.h>
 15  #include <init.h>
 16  #include <interfaces/handler.h>
 17  #include <interfaces/init.h>
 18  #include <interfaces/node.h>
 19  #include <logging.h>
 20  #include <node/context.h>
 21  #include <node/interface_ui.h>
 22  #include <noui.h>
 23  #include <qt/bitcoingui.h>
 24  #include <qt/clientmodel.h>
 25  #include <qt/guiconstants.h>
 26  #include <qt/guiutil.h>
 27  #include <qt/initexecutor.h>
 28  #include <qt/intro.h>
 29  #include <qt/networkstyle.h>
 30  #include <qt/optionsmodel.h>
 31  #include <qt/platformstyle.h>
 32  #include <qt/splashscreen.h>
 33  #include <qt/utilitydialog.h>
 34  #include <qt/winshutdownmonitor.h>
 35  #include <uint256.h>
 36  #include <util/exception.h>
 37  #include <util/string.h>
 38  #include <util/threadnames.h>
 39  #include <util/translation.h>
 40  #include <validation.h>
 41  
 42  #ifdef ENABLE_WALLET
 43  #include <qt/paymentserver.h>
 44  #include <qt/walletcontroller.h>
 45  #include <qt/walletmodel.h>
 46  #include <wallet/types.h>
 47  #endif // ENABLE_WALLET
 48  
 49  #include <boost/signals2/connection.hpp>
 50  #include <chrono>
 51  #include <memory>
 52  
 53  #include <QApplication>
 54  #include <QDebug>
 55  #include <QLatin1String>
 56  #include <QLibraryInfo>
 57  #include <QLocale>
 58  #include <QMessageBox>
 59  #include <QSettings>
 60  #include <QThread>
 61  #include <QTimer>
 62  #include <QTranslator>
 63  #include <QWindow>
 64  
 65  #if defined(QT_STATICPLUGIN)
 66  #include <QtPlugin>
 67  #if defined(QT_QPA_PLATFORM_XCB)
 68  Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
 69  #elif defined(QT_QPA_PLATFORM_WINDOWS)
 70  Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
 71  Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);
 72  #elif defined(QT_QPA_PLATFORM_COCOA)
 73  Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
 74  Q_IMPORT_PLUGIN(QMacStylePlugin);
 75  #elif defined(QT_QPA_PLATFORM_ANDROID)
 76  Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin)
 77  #endif
 78  #endif
 79  
 80  // Declare meta types used for QMetaObject::invokeMethod
 81  Q_DECLARE_METATYPE(bool*)
 82  Q_DECLARE_METATYPE(CAmount)
 83  Q_DECLARE_METATYPE(SynchronizationState)
 84  Q_DECLARE_METATYPE(SyncType)
 85  Q_DECLARE_METATYPE(uint256)
 86  #ifdef ENABLE_WALLET
 87  Q_DECLARE_METATYPE(wallet::AddressPurpose)
 88  #endif // ENABLE_WALLET
 89  
 90  static void RegisterMetaTypes()
 91  {
 92      // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
 93      qRegisterMetaType<bool*>();
 94      qRegisterMetaType<SynchronizationState>();
 95      qRegisterMetaType<SyncType>();
 96    #ifdef ENABLE_WALLET
 97      qRegisterMetaType<WalletModel*>();
 98      qRegisterMetaType<wallet::AddressPurpose>();
 99    #endif // ENABLE_WALLET
100      // Register typedefs (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType)
101      // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
102      qRegisterMetaType<CAmount>("CAmount");
103      qRegisterMetaType<size_t>("size_t");
104  
105      qRegisterMetaType<std::function<void()>>("std::function<void()>");
106      qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
107      qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
108  
109  #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
110      qRegisterMetaTypeStreamOperators<BitcoinUnit>("BitcoinUnit");
111  #else
112      qRegisterMetaType<BitcoinUnit>("BitcoinUnit");
113  #endif
114  }
115  
116  static QString GetLangTerritory()
117  {
118      QSettings settings;
119      // Get desired locale (e.g. "de_DE")
120      // 1) System default language
121      QString lang_territory = QLocale::system().name();
122      // 2) Language from QSettings
123      QString lang_territory_qsettings = settings.value("language", "").toString();
124      if(!lang_territory_qsettings.isEmpty())
125          lang_territory = lang_territory_qsettings;
126      // 3) -lang command line argument
127      lang_territory = QString::fromStdString(gArgs.GetArg("-lang", lang_territory.toStdString()));
128      return lang_territory;
129  }
130  
131  /** Set up translations */
132  static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
133  {
134      // Remove old translators
135      QApplication::removeTranslator(&qtTranslatorBase);
136      QApplication::removeTranslator(&qtTranslator);
137      QApplication::removeTranslator(&translatorBase);
138      QApplication::removeTranslator(&translator);
139  
140      // Get desired locale (e.g. "de_DE")
141      // 1) System default language
142      QString lang_territory = GetLangTerritory();
143  
144      // Convert to "de" only by truncating "_DE"
145      QString lang = lang_territory;
146      lang.truncate(lang_territory.lastIndexOf('_'));
147  
148      // Load language files for configured locale:
149      // - First load the translator for the base language, without territory
150      // - Then load the more specific locale translator
151  
152  #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
153      const QString translation_path{QLibraryInfo::location(QLibraryInfo::TranslationsPath)};
154  #else
155      const QString translation_path{QLibraryInfo::path(QLibraryInfo::TranslationsPath)};
156  #endif
157      // Load e.g. qt_de.qm
158      if (qtTranslatorBase.load("qt_" + lang, translation_path)) {
159          QApplication::installTranslator(&qtTranslatorBase);
160      }
161  
162      // Load e.g. qt_de_DE.qm
163      if (qtTranslator.load("qt_" + lang_territory, translation_path)) {
164          QApplication::installTranslator(&qtTranslator);
165      }
166  
167      // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
168      if (translatorBase.load(lang, ":/translations/")) {
169          QApplication::installTranslator(&translatorBase);
170      }
171  
172      // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
173      if (translator.load(lang_territory, ":/translations/")) {
174          QApplication::installTranslator(&translator);
175      }
176  }
177  
178  static bool ErrorSettingsRead(const bilingual_str& error, const std::vector<std::string>& details)
179  {
180      QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Reset | QMessageBox::Abort);
181      /*: Explanatory text shown on startup when the settings file cannot be read.
182        Prompts user to make a choice between resetting or aborting. */
183      messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?"));
184      messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(details)));
185      messagebox.setTextFormat(Qt::PlainText);
186      messagebox.setDefaultButton(QMessageBox::Reset);
187      switch (messagebox.exec()) {
188      case QMessageBox::Reset:
189          return false;
190      case QMessageBox::Abort:
191          return true;
192      default:
193          assert(false);
194      }
195  }
196  
197  static void ErrorSettingsWrite(const bilingual_str& error, const std::vector<std::string>& details)
198  {
199      QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Ok);
200      /*: Explanatory text shown on startup when the settings file could not be written.
201          Prompts user to check that we have the ability to write to the file.
202          Explains that the user has the option of running without a settings file.*/
203      messagebox.setInformativeText(QObject::tr("A fatal error occurred. Check that settings file is writable, or try running with -nosettings."));
204      messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(details)));
205      messagebox.setTextFormat(Qt::PlainText);
206      messagebox.setDefaultButton(QMessageBox::Ok);
207      messagebox.exec();
208  }
209  
210  /* qDebug() message handler --> debug.log */
211  void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
212  {
213      Q_UNUSED(context);
214      if (type == QtDebugMsg) {
215          LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString());
216      } else {
217          LogPrintf("GUI: %s\n", msg.toStdString());
218      }
219  }
220  
221  static int qt_argc = 1;
222  static const char* qt_argv = "bitcoin-qt";
223  
224  BitcoinApplication::BitcoinApplication()
225      : QApplication(qt_argc, const_cast<char**>(&qt_argv))
226  {
227      // Qt runs setlocale(LC_ALL, "") on initialization.
228      RegisterMetaTypes();
229      setQuitOnLastWindowClosed(false);
230  }
231  
232  void BitcoinApplication::setupPlatformStyle()
233  {
234      // UI per-platform customization
235      // This must be done inside the BitcoinApplication constructor, or after it, because
236      // PlatformStyle::instantiate requires a QApplication
237      std::string platformName;
238      platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
239      platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName));
240      if (!platformStyle) // Fall back to "other" if specified name not found
241          platformStyle = PlatformStyle::instantiate("other");
242      assert(platformStyle);
243  }
244  
245  BitcoinApplication::~BitcoinApplication()
246  {
247      m_executor.reset();
248  
249      delete window;
250      window = nullptr;
251      delete platformStyle;
252      platformStyle = nullptr;
253  }
254  
255  #ifdef ENABLE_WALLET
256  void BitcoinApplication::createPaymentServer()
257  {
258      paymentServer = new PaymentServer(this);
259  }
260  #endif
261  
262  bool BitcoinApplication::createOptionsModel(bool resetSettings)
263  {
264      optionsModel = new OptionsModel(node(), this);
265      if (resetSettings) {
266          optionsModel->Reset();
267      }
268      bilingual_str error;
269      if (!optionsModel->Init(error)) {
270          fs::path settings_path;
271          if (gArgs.GetSettingsPath(&settings_path)) {
272              error += Untranslated("\n");
273              std::string quoted_path = strprintf("%s", fs::quoted(fs::PathToString(settings_path)));
274              error.original += strprintf("Settings file %s might be corrupt or invalid.", quoted_path);
275              error.translated += tr("Settings file %1 might be corrupt or invalid.").arg(QString::fromStdString(quoted_path)).toStdString();
276          }
277          InitError(error);
278          QMessageBox::critical(nullptr, PACKAGE_NAME, QString::fromStdString(error.translated));
279          return false;
280      }
281      return true;
282  }
283  
284  void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
285  {
286      window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
287      connect(window, &BitcoinGUI::quitRequested, this, &BitcoinApplication::requestShutdown);
288  
289      pollShutdownTimer = new QTimer(window);
290      connect(pollShutdownTimer, &QTimer::timeout, [this]{
291          if (!QApplication::activeModalWidget()) {
292              window->detectShutdown();
293          }
294      });
295  }
296  
297  void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
298  {
299      assert(!m_splash);
300      m_splash = new SplashScreen(networkStyle);
301      m_splash->show();
302  }
303  
304  void BitcoinApplication::createNode(interfaces::Init& init)
305  {
306      assert(!m_node);
307      m_node = init.makeNode();
308      if (m_splash) m_splash->setNode(*m_node);
309  }
310  
311  bool BitcoinApplication::baseInitialize()
312  {
313      return node().baseInitialize();
314  }
315  
316  void BitcoinApplication::startThread()
317  {
318      assert(!m_executor);
319      m_executor.emplace(node());
320  
321      /*  communication to and from thread */
322      connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
323      connect(&m_executor.value(), &InitExecutor::shutdownResult, this, [] {
324          QCoreApplication::exit(0);
325      });
326      connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
327      connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
328      connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
329  }
330  
331  void BitcoinApplication::parameterSetup()
332  {
333      // Default printtoconsole to false for the GUI. GUI programs should not
334      // print to the console unnecessarily.
335      gArgs.SoftSetBoolArg("-printtoconsole", false);
336  
337      InitLogging(gArgs);
338      InitParameterInteraction(gArgs);
339  }
340  
341  void BitcoinApplication::InitPruneSetting(int64_t prune_MiB)
342  {
343      optionsModel->SetPruneTargetGB(PruneMiBtoGB(prune_MiB));
344  }
345  
346  void BitcoinApplication::requestInitialize()
347  {
348      qDebug() << __func__ << ": Requesting initialize";
349      startThread();
350      Q_EMIT requestedInitialize();
351  }
352  
353  void BitcoinApplication::requestShutdown()
354  {
355      for (const auto w : QGuiApplication::topLevelWindows()) {
356          w->hide();
357      }
358  
359      delete m_splash;
360      m_splash = nullptr;
361  
362      // Show a simple window indicating shutdown status
363      // Do this first as some of the steps may take some time below,
364      // for example the RPC console may still be executing a command.
365      shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
366  
367      qDebug() << __func__ << ": Requesting shutdown";
368  
369      // Must disconnect node signals otherwise current thread can deadlock since
370      // no event loop is running.
371      window->unsubscribeFromCoreSignals();
372      // Request node shutdown, which can interrupt long operations, like
373      // rescanning a wallet.
374      node().startShutdown();
375      // Prior to unsetting the client model, stop listening backend signals
376      if (clientModel) {
377          clientModel->stop();
378      }
379  
380      // Unsetting the client model can cause the current thread to wait for node
381      // to complete an operation, like wait for a RPC execution to complete.
382      window->setClientModel(nullptr);
383      pollShutdownTimer->stop();
384  
385  #ifdef ENABLE_WALLET
386      // Delete wallet controller here manually, instead of relying on Qt object
387      // tracking (https://doc.qt.io/qt-5/objecttrees.html). This makes sure
388      // walletmodel m_handle_* notification handlers are deleted before wallets
389      // are unloaded, which can simplify wallet implementations. It also avoids
390      // these notifications having to be handled while GUI objects are being
391      // destroyed, making GUI code less fragile as well.
392      delete m_wallet_controller;
393      m_wallet_controller = nullptr;
394  #endif // ENABLE_WALLET
395  
396      delete clientModel;
397      clientModel = nullptr;
398  
399      // Request shutdown from core thread
400      Q_EMIT requestedShutdown();
401  }
402  
403  void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info)
404  {
405      qDebug() << __func__ << ": Initialization result: " << success;
406  
407      if (success) {
408          delete m_splash;
409          m_splash = nullptr;
410  
411          // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
412          qInfo() << "Platform customization:" << platformStyle->getName();
413          clientModel = new ClientModel(node(), optionsModel);
414          window->setClientModel(clientModel, &tip_info);
415  
416          // If '-min' option passed, start window minimized (iconified) or minimized to tray
417          bool start_minimized = gArgs.GetBoolArg("-min", false);
418  #ifdef ENABLE_WALLET
419          if (WalletModel::isWalletEnabled()) {
420              m_wallet_controller = new WalletController(*clientModel, platformStyle, this);
421              window->setWalletController(m_wallet_controller, /*show_loading_minimized=*/start_minimized);
422              if (paymentServer) {
423                  paymentServer->setOptionsModel(optionsModel);
424              }
425          }
426  #endif // ENABLE_WALLET
427  
428          // Show or minimize window
429          if (!start_minimized) {
430              window->show();
431          } else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) {
432              // do nothing as the window is managed by the tray icon
433          } else {
434              window->showMinimized();
435          }
436          Q_EMIT windowShown(window);
437  
438  #ifdef ENABLE_WALLET
439          // Now that initialization/startup is done, process any command-line
440          // bitcoin: URIs or payment requests:
441          if (paymentServer) {
442              connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &BitcoinGUI::handlePaymentRequest);
443              connect(window, &BitcoinGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile);
444              connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) {
445                  window->message(title, message, style);
446              });
447              QTimer::singleShot(100ms, paymentServer, &PaymentServer::uiReady);
448          }
449  #endif
450          pollShutdownTimer->start(SHUTDOWN_POLLING_DELAY);
451      } else {
452          requestShutdown();
453      }
454  }
455  
456  void BitcoinApplication::handleRunawayException(const QString &message)
457  {
458      QMessageBox::critical(
459          nullptr, tr("Runaway exception"),
460          tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) +
461          QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
462      ::exit(EXIT_FAILURE);
463  }
464  
465  void BitcoinApplication::handleNonFatalException(const QString& message)
466  {
467      assert(QThread::currentThread() == thread());
468      QMessageBox::warning(
469          nullptr, tr("Internal error"),
470          tr("An internal error occurred. %1 will attempt to continue safely. This is "
471             "an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) +
472          QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
473  }
474  
475  WId BitcoinApplication::getMainWinId() const
476  {
477      if (!window)
478          return 0;
479  
480      return window->winId();
481  }
482  
483  bool BitcoinApplication::event(QEvent* e)
484  {
485      if (e->type() == QEvent::Quit) {
486          requestShutdown();
487          return true;
488      }
489  
490      return QApplication::event(e);
491  }
492  
493  static void SetupUIArgs(ArgsManager& argsman)
494  {
495      argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
496      argsman.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
497      argsman.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
498      argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
499      argsman.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
500      argsman.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
501  }
502  
503  int GuiMain(int argc, char* argv[])
504  {
505  #ifdef WIN32
506      common::WinCmdLineArgs winArgs;
507      std::tie(argc, argv) = winArgs.get();
508  #endif
509  
510      std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
511  
512      SetupEnvironment();
513      util::ThreadSetInternalName("main");
514  
515      // Subscribe to global signals from core
516      boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
517      boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
518      boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage);
519  
520      // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
521  
522      /// 1. Basic Qt initialization (not dependent on parameters or configuration)
523      Q_INIT_RESOURCE(bitcoin);
524      Q_INIT_RESOURCE(bitcoin_locale);
525  
526  #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
527      // Generate high-dpi pixmaps
528      QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
529      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
530  #endif
531  
532  #if defined(QT_QPA_PLATFORM_ANDROID)
533      QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
534      QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
535      QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
536  #endif
537  
538      BitcoinApplication app;
539      GUIUtil::LoadFont(QStringLiteral(":/fonts/monospace"));
540  
541      /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
542      // Command-line options take precedence:
543      SetupServerArgs(gArgs);
544      SetupUIArgs(gArgs);
545      std::string error;
546      if (!gArgs.ParseParameters(argc, argv, error)) {
547          InitError(strprintf(Untranslated("Error parsing command line arguments: %s"), error));
548          // Create a message box, because the gui has neither been created nor has subscribed to core signals
549          QMessageBox::critical(nullptr, PACKAGE_NAME,
550              // message cannot be translated because translations have not been initialized
551              QString::fromStdString("Error parsing command line arguments: %1.").arg(QString::fromStdString(error)));
552          return EXIT_FAILURE;
553      }
554  
555      // Error out when loose non-argument tokens are encountered on command line
556      // However, allow BIP-21 URIs only if no options follow
557      bool payment_server_token_seen = false;
558      for (int i = 1; i < argc; i++) {
559          QString arg(argv[i]);
560          bool invalid_token = !arg.startsWith("-");
561  #ifdef ENABLE_WALLET
562          if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) {
563              invalid_token &= false;
564              payment_server_token_seen = true;
565          }
566  #endif
567          if (payment_server_token_seen && arg.startsWith("-")) {
568              InitError(Untranslated(strprintf("Options ('%s') cannot follow a BIP-21 payment URI", argv[i])));
569              QMessageBox::critical(nullptr, PACKAGE_NAME,
570                                    // message cannot be translated because translations have not been initialized
571                                    QString::fromStdString("Options ('%1') cannot follow a BIP-21 payment URI").arg(QString::fromStdString(argv[i])));
572              return EXIT_FAILURE;
573          }
574          if (invalid_token) {
575              InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoin-qt -h for a list of options.", argv[i])));
576              QMessageBox::critical(nullptr, PACKAGE_NAME,
577                                    // message cannot be translated because translations have not been initialized
578                                    QString::fromStdString("Command line contains unexpected token '%1', see bitcoin-qt -h for a list of options.").arg(QString::fromStdString(argv[i])));
579              return EXIT_FAILURE;
580          }
581      }
582  
583      // Now that the QApplication is setup and we have parsed our parameters, we can set the platform style
584      app.setupPlatformStyle();
585  
586      /// 3. Application identification
587      // must be set before OptionsModel is initialized or translations are loaded,
588      // as it is used to locate QSettings
589      QApplication::setOrganizationName(QAPP_ORG_NAME);
590      QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
591      QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
592  
593      /// 4. Initialization of translations, so that intro dialog is in user's language
594      // Now that QSettings are accessible, initialize translations
595      QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
596      initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
597  
598      // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
599      // but before showing splash screen.
600      if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
601          HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
602          help.showOrPrint();
603          return EXIT_SUCCESS;
604      }
605  
606      // Install global event filter that makes sure that long tooltips can be word-wrapped
607      app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
608  
609      /// 5. Now that settings and translations are available, ask user for data directory
610      // User language is set up: pick a data directory
611      bool did_show_intro = false;
612      int64_t prune_MiB = 0;  // Intro dialog prune configuration
613      // Gracefully exit if the user cancels
614      if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS;
615  
616      /// 6-7. Parse bitcoin.conf, determine network, switch to network specific
617      /// options, and create datadir and settings.json.
618      // - Do not call gArgs.GetDataDirNet() before this step finishes
619      // - Do not call Params() before this step
620      // - QSettings() will use the new application name after this, resulting in network-specific settings
621      // - Needs to be done before createOptionsModel
622      if (auto error = common::InitConfig(gArgs, ErrorSettingsRead)) {
623          InitError(error->message, error->details);
624          if (error->status == common::ConfigStatus::FAILED_WRITE) {
625              // Show a custom error message to provide more information in the
626              // case of a datadir write error.
627              ErrorSettingsWrite(error->message, error->details);
628          } else if (error->status != common::ConfigStatus::ABORTED) {
629              // Show a generic message in other cases, and no additional error
630              // message in the case of a read error if the user decided to abort.
631              QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(QString::fromStdString(error->message.translated)));
632          }
633          return EXIT_FAILURE;
634      }
635  #ifdef ENABLE_WALLET
636      // Parse URIs on command line
637      PaymentServer::ipcParseCommandLine(argc, argv);
638  #endif
639  
640      QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().GetChainType()));
641      assert(!networkStyle.isNull());
642      // Allow for separate UI settings for testnets
643      QApplication::setApplicationName(networkStyle->getAppName());
644      // Re-initialize translations after changing application name (language in network-specific settings can be different)
645      initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
646  
647  #ifdef ENABLE_WALLET
648      /// 8. URI IPC sending
649      // - Do this early as we don't want to bother initializing if we are just calling IPC
650      // - Do this *after* setting up the data directory, as the data directory hash is used in the name
651      // of the server.
652      // - Do this after creating app and setting up translations, so errors are
653      // translated properly.
654      if (PaymentServer::ipcSendCommandLine())
655          exit(EXIT_SUCCESS);
656  
657      // Start up the payment server early, too, so impatient users that click on
658      // bitcoin: links repeatedly have their payment requests routed to this process:
659      if (WalletModel::isWalletEnabled()) {
660          app.createPaymentServer();
661      }
662  #endif // ENABLE_WALLET
663  
664      /// 9. Main GUI initialization
665      // Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
666      app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
667  #if defined(Q_OS_WIN)
668      // Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
669      // Note: it is safe to call app.node() in the lambda below despite the fact
670      // that app.createNode() hasn't been called yet, because native events will
671      // not be processed until the Qt event loop is executed.
672      qApp->installNativeEventFilter(new WinShutdownMonitor([&app] { app.node().startShutdown(); }));
673  #endif
674      // Install qDebug() message handler to route to debug.log
675      qInstallMessageHandler(DebugMessageHandler);
676      // Allow parameter interaction before we create the options model
677      app.parameterSetup();
678      GUIUtil::LogQtInfo();
679  
680      if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
681          app.createSplashScreen(networkStyle.data());
682  
683      app.createNode(*init);
684  
685      // Load GUI settings from QSettings
686      if (!app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false))) {
687          return EXIT_FAILURE;
688      }
689  
690      if (did_show_intro) {
691          // Store intro dialog settings other than datadir (network specific)
692          app.InitPruneSetting(prune_MiB);
693      }
694  
695      try
696      {
697          app.createWindow(networkStyle.data());
698          // Perform base initialization before spinning up initialization/shutdown thread
699          // This is acceptable because this function only contains steps that are quick to execute,
700          // so the GUI thread won't be held up.
701          if (app.baseInitialize()) {
702              app.requestInitialize();
703  #if defined(Q_OS_WIN)
704              WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
705  #endif
706              app.exec();
707          } else {
708              // A dialog with detailed error will have been shown by InitError()
709              return EXIT_FAILURE;
710          }
711      } catch (const std::exception& e) {
712          PrintExceptionContinue(&e, "Runaway exception");
713          app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
714      } catch (...) {
715          PrintExceptionContinue(nullptr, "Runaway exception");
716          app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
717      }
718      return app.node().getExitStatus();
719  }