bitcoin.cpp raw

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