Bitcoin Core  29.1.0
P2P Digital Currency
optionsmodel.cpp
Go to the documentation of this file.
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 #include <bitcoin-build-config.h> // IWYU pragma: keep
6 
7 #include <qt/optionsmodel.h>
8 
9 #include <qt/bitcoinunits.h>
10 #include <qt/guiconstants.h>
11 #include <qt/guiutil.h>
12 
13 #include <common/args.h>
14 #include <interfaces/node.h>
15 #include <mapport.h>
16 #include <net.h>
17 #include <netbase.h>
18 #include <node/caches.h>
20 #include <util/string.h>
21 #include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
22 #include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
23 
24 #include <QDebug>
25 #include <QLatin1Char>
26 #include <QSettings>
27 #include <QStringList>
28 #include <QVariant>
29 
30 #include <univalue.h>
31 
32 const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
33 
34 static QString GetDefaultProxyAddress();
35 
37 static const char* SettingName(OptionsModel::OptionID option)
38 {
39  switch (option) {
40  case OptionsModel::DatabaseCache: return "dbcache";
41  case OptionsModel::ThreadsScriptVerif: return "par";
42  case OptionsModel::SpendZeroConfChange: return "spendzeroconfchange";
43  case OptionsModel::ExternalSignerPath: return "signer";
44  case OptionsModel::MapPortNatpmp: return "natpmp";
45  case OptionsModel::Listen: return "listen";
46  case OptionsModel::Server: return "server";
47  case OptionsModel::PruneSize: return "prune";
48  case OptionsModel::Prune: return "prune";
49  case OptionsModel::ProxyIP: return "proxy";
50  case OptionsModel::ProxyPort: return "proxy";
51  case OptionsModel::ProxyUse: return "proxy";
52  case OptionsModel::ProxyIPTor: return "onion";
53  case OptionsModel::ProxyPortTor: return "onion";
54  case OptionsModel::ProxyUseTor: return "onion";
55  case OptionsModel::Language: return "lang";
56  default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option));
57  }
58 }
59 
61 static void UpdateRwSetting(interfaces::Node& node, OptionsModel::OptionID option, const std::string& suffix, const common::SettingsValue& value)
62 {
63  if (value.isNum() &&
64  (option == OptionsModel::DatabaseCache ||
66  option == OptionsModel::Prune ||
67  option == OptionsModel::PruneSize)) {
68  // Write certain old settings as strings, even though they are numbers,
69  // because Bitcoin 22.x releases try to read these specific settings as
70  // strings in addOverriddenOption() calls at startup, triggering
71  // uncaught exceptions in UniValue::get_str(). These errors were fixed
72  // in later releases by https://github.com/bitcoin/bitcoin/pull/24498.
73  // If new numeric settings are added, they can be written as numbers
74  // instead of strings, because bitcoin 22.x will not try to read these.
75  node.updateRwSetting(SettingName(option) + suffix, value.getValStr());
76  } else {
77  node.updateRwSetting(SettingName(option) + suffix, value);
78  }
79 }
80 
82 static common::SettingsValue PruneSetting(bool prune_enabled, int prune_size_gb)
83 {
84  assert(!prune_enabled || prune_size_gb >= 1); // PruneSizeGB and ParsePruneSizeGB never return less
85  return prune_enabled ? PruneGBtoMiB(prune_size_gb) : 0;
86 }
87 
89 static bool PruneEnabled(const common::SettingsValue& prune_setting)
90 {
91  // -prune=1 setting is manual pruning mode, so disabled for purposes of the gui
92  return SettingToInt(prune_setting, 0) > 1;
93 }
94 
97 static int PruneSizeGB(const common::SettingsValue& prune_setting)
98 {
99  int value = SettingToInt(prune_setting, 0);
100  return value > 1 ? PruneMiBtoGB(value) : DEFAULT_PRUNE_TARGET_GB;
101 }
102 
106 static int ParsePruneSizeGB(const QVariant& prune_size)
107 {
108  return std::max(1, prune_size.toInt());
109 }
110 
111 struct ProxySetting {
112  bool is_set;
113  QString ip;
114  QString port;
115 };
116 static ProxySetting ParseProxyString(const std::string& proxy);
117 static std::string ProxyString(bool is_set, QString ip, QString port);
118 
119 static const QLatin1String fontchoice_str_embedded{"embedded"};
120 static const QLatin1String fontchoice_str_best_system{"best_system"};
121 static const QString fontchoice_str_custom_prefix{QStringLiteral("custom, ")};
122 
124 {
125  if (std::holds_alternative<FontChoiceAbstract>(f)) {
126  if (f == UseBestSystemFont) {
128  } else {
130  }
131  }
132  return fontchoice_str_custom_prefix + std::get<QFont>(f).toString();
133 }
134 
136 {
137  if (s == fontchoice_str_best_system) {
139  } else if (s == fontchoice_str_embedded) {
141  } else if (s.startsWith(fontchoice_str_custom_prefix)) {
142  QFont f;
143  f.fromString(s.mid(fontchoice_str_custom_prefix.size()));
144  return f;
145  } else {
146  return FontChoiceAbstract::EmbeddedFont; // default
147  }
148 }
149 
151  QAbstractListModel(parent), m_node{node}
152 {
153 }
154 
155 void OptionsModel::addOverriddenOption(const std::string &option)
156 {
157  strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " ";
158 }
159 
160 // Writes all missing QSettings with their default values
162 {
163  // Initialize display settings from stored settings.
164  language = QString::fromStdString(SettingToString(node().getPersistentSetting("lang"), ""));
165 
166  checkAndMigrate();
167 
168  QSettings settings;
169 
170  // Ensure restart flag is unset on client startup
171  setRestartRequired(false);
172 
173  // These are Qt-only settings:
174 
175  // Window
176  if (!settings.contains("fHideTrayIcon")) {
177  settings.setValue("fHideTrayIcon", false);
178  }
179  m_show_tray_icon = !settings.value("fHideTrayIcon").toBool();
181 
182  if (!settings.contains("fMinimizeToTray"))
183  settings.setValue("fMinimizeToTray", false);
184  fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && m_show_tray_icon;
185 
186  if (!settings.contains("fMinimizeOnClose"))
187  settings.setValue("fMinimizeOnClose", false);
188  fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
189 
190  // Display
191  if (!settings.contains("DisplayBitcoinUnit")) {
192  settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC));
193  }
194  QVariant unit = settings.value("DisplayBitcoinUnit");
195  if (unit.canConvert<BitcoinUnit>()) {
196  m_display_bitcoin_unit = unit.value<BitcoinUnit>();
197  } else {
199  settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit));
200  }
201 
202  if (!settings.contains("strThirdPartyTxUrls"))
203  settings.setValue("strThirdPartyTxUrls", "");
204  strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
205 
206  if (!settings.contains("fCoinControlFeatures"))
207  settings.setValue("fCoinControlFeatures", false);
208  fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
209 
210  if (!settings.contains("enable_psbt_controls")) {
211  settings.setValue("enable_psbt_controls", false);
212  }
213  m_enable_psbt_controls = settings.value("enable_psbt_controls", false).toBool();
214 
215  // These are shared with the core or have a command-line parameter
216  // and we want command-line parameters to overwrite the GUI settings.
219  std::string setting = SettingName(option);
220  if (node().isSettingIgnored(setting)) addOverriddenOption("-" + setting);
221  try {
222  getOption(option);
223  } catch (const std::exception& e) {
224  // This handles exceptions thrown by univalue that can happen if
225  // settings in settings.json don't have the expected types.
226  error.original = strprintf("Could not read setting \"%s\", %s.", setting, e.what());
227  error.translated = tr("Could not read setting \"%1\", %2.").arg(QString::fromStdString(setting), e.what()).toStdString();
228  return false;
229  }
230  }
231 
232  // If setting doesn't exist create it with defaults.
233 
234  // Main
235  if (!settings.contains("strDataDir"))
236  settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
237 
238  // Wallet
239 #ifdef ENABLE_WALLET
240  if (!settings.contains("SubFeeFromAmount")) {
241  settings.setValue("SubFeeFromAmount", false);
242  }
243  m_sub_fee_from_amount = settings.value("SubFeeFromAmount", false).toBool();
244 #endif
245 
246  // Display
247  if (settings.contains("FontForMoney")) {
248  m_font_money = FontChoiceFromString(settings.value("FontForMoney").toString());
249  } else if (settings.contains("UseEmbeddedMonospacedFont")) {
250  if (settings.value("UseEmbeddedMonospacedFont").toBool()) {
252  } else {
254  }
255  }
257 
258  m_mask_values = settings.value("mask_values", false).toBool();
259 
260  return true;
261 }
262 
266 static void CopySettings(QSettings& dst, const QSettings& src)
267 {
268  for (const QString& key : src.allKeys()) {
269  dst.setValue(key, src.value(key));
270  }
271 }
272 
274 static void BackupSettings(const fs::path& filename, const QSettings& src)
275 {
276  qInfo() << "Backing up GUI settings to" << GUIUtil::PathToQString(filename);
277  QSettings dst(GUIUtil::PathToQString(filename), QSettings::IniFormat);
278  dst.clear();
279  CopySettings(dst, src);
280 }
281 
283 {
284  // Backup and reset settings.json
285  node().resetSettings();
286 
287  QSettings settings;
288 
289  // Backup old settings to chain-specific datadir for troubleshooting
290  BackupSettings(gArgs.GetDataDirNet() / "guisettings.ini.bak", settings);
291 
292  // Save the strDataDir setting
293  QString dataDir = GUIUtil::getDefaultDataDirectory();
294  dataDir = settings.value("strDataDir", dataDir).toString();
295 
296  // Remove all entries from our QSettings object
297  settings.clear();
298 
299  // Set strDataDir
300  settings.setValue("strDataDir", dataDir);
301 
302  // Set that this was reset
303  settings.setValue("fReset", true);
304 
305  // default setting for OptionsModel::StartAtStartup - disabled
308 }
309 
310 int OptionsModel::rowCount(const QModelIndex & parent) const
311 {
312  return OptionIDRowCount;
313 }
314 
315 static ProxySetting ParseProxyString(const QString& proxy)
316 {
317  static const ProxySetting default_val = {false, DEFAULT_GUI_PROXY_HOST, QString("%1").arg(DEFAULT_GUI_PROXY_PORT)};
318  // Handle the case that the setting is not set at all
319  if (proxy.isEmpty()) {
320  return default_val;
321  }
322  uint16_t port{0};
323  std::string hostname;
324  if (SplitHostPort(proxy.toStdString(), port, hostname) && port != 0) {
325  // Valid and port within the valid range
326  // Check if the hostname contains a colon, indicating an IPv6 address
327  if (hostname.find(':') != std::string::npos) {
328  hostname = "[" + hostname + "]"; // Wrap IPv6 address in brackets
329  }
330  return {true, QString::fromStdString(hostname), QString::number(port)};
331  } else { // Invalid: return default
332  return default_val;
333  }
334 }
335 
336 static ProxySetting ParseProxyString(const std::string& proxy)
337 {
338  return ParseProxyString(QString::fromStdString(proxy));
339 }
340 
341 static std::string ProxyString(bool is_set, QString ip, QString port)
342 {
343  return is_set ? QString(ip + ":" + port).toStdString() : "";
344 }
345 
346 static QString GetDefaultProxyAddress()
347 {
348  return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
349 }
350 
351 void OptionsModel::SetPruneTargetGB(int prune_target_gb)
352 {
353  const common::SettingsValue cur_value = node().getPersistentSetting("prune");
354  const common::SettingsValue new_value = PruneSetting(prune_target_gb > 0, prune_target_gb);
355 
356  // Force setting to take effect. It is still safe to change the value at
357  // this point because this function is only called after the intro screen is
358  // shown, before the node starts.
359  node().forceSetting("prune", new_value);
360 
361  // Update settings.json if value configured in intro screen is different
362  // from saved value. Avoid writing settings.json if bitcoin.conf value
363  // doesn't need to be overridden.
364  if (PruneEnabled(cur_value) != PruneEnabled(new_value) ||
365  PruneSizeGB(cur_value) != PruneSizeGB(new_value)) {
366  // Call UpdateRwSetting() instead of setOption() to avoid setting
367  // RestartRequired flag
368  UpdateRwSetting(node(), Prune, "", new_value);
369  }
370 
371  // Keep previous pruning size, if pruning was disabled.
372  if (PruneEnabled(cur_value)) {
373  UpdateRwSetting(node(), Prune, "-prev", PruneEnabled(new_value) ? common::SettingsValue{} : cur_value);
374  }
375 }
376 
377 // read QSettings values and return them
378 QVariant OptionsModel::data(const QModelIndex & index, int role) const
379 {
380  if(role == Qt::EditRole)
381  {
382  return getOption(OptionID(index.row()));
383  }
384  return QVariant();
385 }
386 
387 // write QSettings values
388 bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
389 {
390  bool successful = true; /* set to false on parse error */
391  if(role == Qt::EditRole)
392  {
393  successful = setOption(OptionID(index.row()), value);
394  }
395 
396  Q_EMIT dataChanged(index, index);
397 
398  return successful;
399 }
400 
401 // NOLINTNEXTLINE(misc-no-recursion)
402 QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) const
403 {
404  auto setting = [&]{ return node().getPersistentSetting(SettingName(option) + suffix); };
405 
406  QSettings settings;
407  switch (option) {
408  case StartAtStartup:
410  case ShowTrayIcon:
411  return m_show_tray_icon;
412  case MinimizeToTray:
413  return fMinimizeToTray;
414  case MapPortNatpmp:
415  return SettingToBool(setting(), DEFAULT_NATPMP);
416  case MinimizeOnClose:
417  return fMinimizeOnClose;
418 
419  // default proxy
420  case ProxyUse:
421  case ProxyUseTor:
422  return ParseProxyString(SettingToString(setting(), "")).is_set;
423  case ProxyIP:
424  case ProxyIPTor: {
425  ProxySetting proxy = ParseProxyString(SettingToString(setting(), ""));
426  if (proxy.is_set) {
427  return proxy.ip;
428  } else if (suffix.empty()) {
429  return getOption(option, "-prev");
430  } else {
431  return ParseProxyString(GetDefaultProxyAddress().toStdString()).ip;
432  }
433  }
434  case ProxyPort:
435  case ProxyPortTor: {
436  ProxySetting proxy = ParseProxyString(SettingToString(setting(), ""));
437  if (proxy.is_set) {
438  return proxy.port;
439  } else if (suffix.empty()) {
440  return getOption(option, "-prev");
441  } else {
442  return ParseProxyString(GetDefaultProxyAddress().toStdString()).port;
443  }
444  }
445 
446 #ifdef ENABLE_WALLET
447  case SpendZeroConfChange:
449  case ExternalSignerPath:
450  return QString::fromStdString(SettingToString(setting(), ""));
451  case SubFeeFromAmount:
452  return m_sub_fee_from_amount;
453 #endif
454  case DisplayUnit:
455  return QVariant::fromValue(m_display_bitcoin_unit);
456  case ThirdPartyTxUrls:
457  return strThirdPartyTxUrls;
458  case Language:
459  return QString::fromStdString(SettingToString(setting(), ""));
460  case FontForMoney:
461  return QVariant::fromValue(m_font_money);
462  case CoinControlFeatures:
463  return fCoinControlFeatures;
464  case EnablePSBTControls:
465  return settings.value("enable_psbt_controls");
466  case Prune:
467  return PruneEnabled(setting());
468  case PruneSize:
469  return PruneEnabled(setting()) ? PruneSizeGB(setting()) :
470  suffix.empty() ? getOption(option, "-prev") :
472  case DatabaseCache:
473  return qlonglong(SettingToInt(setting(), DEFAULT_DB_CACHE >> 20));
474  case ThreadsScriptVerif:
475  return qlonglong(SettingToInt(setting(), DEFAULT_SCRIPTCHECK_THREADS));
476  case Listen:
477  return SettingToBool(setting(), DEFAULT_LISTEN);
478  case Server:
479  return SettingToBool(setting(), false);
480  case MaskValues:
481  return m_mask_values;
482  default:
483  return QVariant();
484  }
485 }
486 
488 {
489  QFont f;
490  if (std::holds_alternative<FontChoiceAbstract>(fc)) {
492  f.setWeight(QFont::Bold);
493  } else {
494  f = std::get<QFont>(fc);
495  }
496  return f;
497 }
498 
500 {
502 }
503 
504 // NOLINTNEXTLINE(misc-no-recursion)
505 bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::string& suffix)
506 {
507  auto changed = [&] { return value.isValid() && value != getOption(option, suffix); };
508  auto update = [&](const common::SettingsValue& value) { return UpdateRwSetting(node(), option, suffix, value); };
509 
510  bool successful = true; /* set to false on parse error */
511  QSettings settings;
512 
513  switch (option) {
514  case StartAtStartup:
515  successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
516  break;
517  case ShowTrayIcon:
518  m_show_tray_icon = value.toBool();
519  settings.setValue("fHideTrayIcon", !m_show_tray_icon);
521  break;
522  case MinimizeToTray:
523  fMinimizeToTray = value.toBool();
524  settings.setValue("fMinimizeToTray", fMinimizeToTray);
525  break;
526  case MapPortNatpmp: // core option - can be changed on-the-fly
527  if (changed()) {
528  update(value.toBool());
529  node().mapPort(value.toBool());
530  }
531  break;
532  case MinimizeOnClose:
533  fMinimizeOnClose = value.toBool();
534  settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
535  break;
536 
537  // default proxy
538  case ProxyUse:
539  if (changed()) {
540  if (suffix.empty() && !value.toBool()) setOption(option, true, "-prev");
541  update(ProxyString(value.toBool(), getOption(ProxyIP).toString(), getOption(ProxyPort).toString()));
542  if (suffix.empty() && value.toBool()) UpdateRwSetting(node(), option, "-prev", {});
543  if (suffix.empty()) setRestartRequired(true);
544  }
545  break;
546  case ProxyIP:
547  if (changed()) {
548  if (suffix.empty() && !getOption(ProxyUse).toBool()) {
549  setOption(option, value, "-prev");
550  } else {
551  update(ProxyString(true, value.toString(), getOption(ProxyPort).toString()));
552  }
553  if (suffix.empty() && getOption(ProxyUse).toBool()) setRestartRequired(true);
554  }
555  break;
556  case ProxyPort:
557  if (changed()) {
558  if (suffix.empty() && !getOption(ProxyUse).toBool()) {
559  setOption(option, value, "-prev");
560  } else {
561  update(ProxyString(true, getOption(ProxyIP).toString(), value.toString()));
562  }
563  if (suffix.empty() && getOption(ProxyUse).toBool()) setRestartRequired(true);
564  }
565  break;
566 
567  // separate Tor proxy
568  case ProxyUseTor:
569  if (changed()) {
570  if (suffix.empty() && !value.toBool()) setOption(option, true, "-prev");
571  update(ProxyString(value.toBool(), getOption(ProxyIPTor).toString(), getOption(ProxyPortTor).toString()));
572  if (suffix.empty() && value.toBool()) UpdateRwSetting(node(), option, "-prev", {});
573  if (suffix.empty()) setRestartRequired(true);
574  }
575  break;
576  case ProxyIPTor:
577  if (changed()) {
578  if (suffix.empty() && !getOption(ProxyUseTor).toBool()) {
579  setOption(option, value, "-prev");
580  } else {
581  update(ProxyString(true, value.toString(), getOption(ProxyPortTor).toString()));
582  }
583  if (suffix.empty() && getOption(ProxyUseTor).toBool()) setRestartRequired(true);
584  }
585  break;
586  case ProxyPortTor:
587  if (changed()) {
588  if (suffix.empty() && !getOption(ProxyUseTor).toBool()) {
589  setOption(option, value, "-prev");
590  } else {
591  update(ProxyString(true, getOption(ProxyIPTor).toString(), value.toString()));
592  }
593  if (suffix.empty() && getOption(ProxyUseTor).toBool()) setRestartRequired(true);
594  }
595  break;
596 
597 #ifdef ENABLE_WALLET
598  case SpendZeroConfChange:
599  if (changed()) {
600  update(value.toBool());
601  setRestartRequired(true);
602  }
603  break;
604  case ExternalSignerPath:
605  if (changed()) {
606  update(value.toString().toStdString());
607  setRestartRequired(true);
608  }
609  break;
610  case SubFeeFromAmount:
611  m_sub_fee_from_amount = value.toBool();
612  settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
613  break;
614 #endif
615  case DisplayUnit:
616  setDisplayUnit(value);
617  break;
618  case ThirdPartyTxUrls:
619  if (strThirdPartyTxUrls != value.toString()) {
620  strThirdPartyTxUrls = value.toString();
621  settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
622  setRestartRequired(true);
623  }
624  break;
625  case Language:
626  if (changed()) {
627  update(value.toString().toStdString());
628  setRestartRequired(true);
629  }
630  break;
631  case FontForMoney:
632  {
633  const auto& new_font = value.value<FontChoice>();
634  if (m_font_money == new_font) break;
635  settings.setValue("FontForMoney", FontChoiceToString(new_font));
636  m_font_money = new_font;
638  break;
639  }
640  case CoinControlFeatures:
641  fCoinControlFeatures = value.toBool();
642  settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
644  break;
645  case EnablePSBTControls:
646  m_enable_psbt_controls = value.toBool();
647  settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
648  break;
649  case Prune:
650  if (changed()) {
651  if (suffix.empty() && !value.toBool()) setOption(option, true, "-prev");
652  update(PruneSetting(value.toBool(), getOption(PruneSize).toInt()));
653  if (suffix.empty() && value.toBool()) UpdateRwSetting(node(), option, "-prev", {});
654  if (suffix.empty()) setRestartRequired(true);
655  }
656  break;
657  case PruneSize:
658  if (changed()) {
659  if (suffix.empty() && !getOption(Prune).toBool()) {
660  setOption(option, value, "-prev");
661  } else {
662  update(PruneSetting(true, ParsePruneSizeGB(value)));
663  }
664  if (suffix.empty() && getOption(Prune).toBool()) setRestartRequired(true);
665  }
666  break;
667  case DatabaseCache:
668  if (changed()) {
669  update(static_cast<int64_t>(value.toLongLong()));
670  setRestartRequired(true);
671  }
672  break;
673  case ThreadsScriptVerif:
674  if (changed()) {
675  update(static_cast<int64_t>(value.toLongLong()));
676  setRestartRequired(true);
677  }
678  break;
679  case Listen:
680  case Server:
681  if (changed()) {
682  update(value.toBool());
683  setRestartRequired(true);
684  }
685  break;
686  case MaskValues:
687  m_mask_values = value.toBool();
688  settings.setValue("mask_values", m_mask_values);
689  break;
690  default:
691  break;
692  }
693 
694  return successful;
695 }
696 
697 void OptionsModel::setDisplayUnit(const QVariant& new_unit)
698 {
699  if (new_unit.isNull() || new_unit.value<BitcoinUnit>() == m_display_bitcoin_unit) return;
700  m_display_bitcoin_unit = new_unit.value<BitcoinUnit>();
701  QSettings settings;
702  settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit));
704 }
705 
707 {
708  QSettings settings;
709  return settings.setValue("fRestartRequired", fRequired);
710 }
711 
713 {
714  QSettings settings;
715  return settings.value("fRestartRequired", false).toBool();
716 }
717 
719 {
720  return gArgs.GetArg("-signer", "") != "";
721 }
722 
724 {
725  // Migration of default values
726  // Check if the QSettings container was already loaded with this client version
727  QSettings settings;
728  static const char strSettingsVersionKey[] = "nSettingsVersion";
729  int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0;
730  if (settingsVersion < CLIENT_VERSION)
731  {
732  // -dbcache was bumped from 100 to 300 in 0.13
733  // see https://github.com/bitcoin/bitcoin/pull/8273
734  // force people to upgrade to the new value if they are using 100MB
735  if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
736  settings.setValue("nDatabaseCache", (qint64)(DEFAULT_DB_CACHE >> 20));
737 
738  settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
739  }
740 
741  // Overwrite the 'addrProxy' setting in case it has been set to an illegal
742  // default value (see issue #12623; PR #12650).
743  if (settings.contains("addrProxy") && settings.value("addrProxy").toString().endsWith("%2")) {
744  settings.setValue("addrProxy", GetDefaultProxyAddress());
745  }
746 
747  // Overwrite the 'addrSeparateProxyTor' setting in case it has been set to an illegal
748  // default value (see issue #12623; PR #12650).
749  if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) {
750  settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
751  }
752 
753  // Migrate and delete legacy GUI settings that have now moved to <datadir>/settings.json.
754  auto migrate_setting = [&](OptionID option, const QString& qt_name) {
755  if (!settings.contains(qt_name)) return;
756  QVariant value = settings.value(qt_name);
757  if (node().getPersistentSetting(SettingName(option)).isNull()) {
758  if (option == ProxyIP) {
759  ProxySetting parsed = ParseProxyString(value.toString());
760  setOption(ProxyIP, parsed.ip);
761  setOption(ProxyPort, parsed.port);
762  } else if (option == ProxyIPTor) {
763  ProxySetting parsed = ParseProxyString(value.toString());
764  setOption(ProxyIPTor, parsed.ip);
765  setOption(ProxyPortTor, parsed.port);
766  } else {
767  setOption(option, value);
768  }
769  }
770  settings.remove(qt_name);
771  };
772 
773  migrate_setting(DatabaseCache, "nDatabaseCache");
774  migrate_setting(ThreadsScriptVerif, "nThreadsScriptVerif");
775 #ifdef ENABLE_WALLET
776  migrate_setting(SpendZeroConfChange, "bSpendZeroConfChange");
777  migrate_setting(ExternalSignerPath, "external_signer_path");
778 #endif
779  migrate_setting(MapPortNatpmp, "fUseNatpmp");
780  migrate_setting(Listen, "fListen");
781  migrate_setting(Server, "server");
782  migrate_setting(PruneSize, "nPruneSize");
783  migrate_setting(Prune, "bPrune");
784  migrate_setting(ProxyIP, "addrProxy");
785  migrate_setting(ProxyUse, "fUseProxy");
786  migrate_setting(ProxyIPTor, "addrSeparateProxyTor");
787  migrate_setting(ProxyUseTor, "fUseSeparateProxyTor");
788  migrate_setting(Language, "language");
789 
790  // In case migrating QSettings caused any settings value to change, rerun
791  // parameter interaction code to update other settings. This is particularly
792  // important for the -listen setting, which should cause -listenonion
793  // and other settings to default to false if it was set to false.
794  // (https://github.com/bitcoin-core/gui/issues/567).
796 }
virtual common::SettingsValue getPersistentSetting(const std::string &name)=0
Return setting value from <datadir>/settings.json or bitcoin.conf.
static std::string ProxyString(bool is_set, QString ip, QString port)
static void UpdateRwSetting(interfaces::Node &node, OptionsModel::OptionID option, const std::string &suffix, const common::SettingsValue &value)
Call node.updateRwSetting() with Bitcoin 22.x workaround.
static const char * SettingName(OptionsModel::OptionID option)
Map GUI option ID to node setting name.
virtual void resetSettings()=0
Clear all settings in <datadir>/settings.json and store a backup of previous settings in <datadir>/se...
Unit
Bitcoin units.
Definition: bitcoinunits.h:42
static constexpr int DEFAULT_PRUNE_TARGET_GB
Definition: guiconstants.h:61
static void BackupSettings(const fs::path &filename, const QSettings &src)
Back up a QSettings to an ini-formatted file.
void addOverriddenOption(const std::string &option)
assert(!tx.IsCoinBase())
FontChoice m_font_money
Definition: optionsmodel.h:132
Bilingual messages:
Definition: translation.h:24
bool SplitHostPort(std::string_view in, uint16_t &portOut, std::string &hostOut)
Splits socket address string into host string and port value.
node::NodeContext m_node
Definition: bitcoin-gui.cpp:42
bool m_mask_values
Definition: optionsmodel.h:136
bool hasSigner()
Whether -signer was set or not.
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
static common::SettingsValue PruneSetting(bool prune_enabled, int prune_size_gb)
Convert enabled/size values to bitcoin -prune setting.
static int ParsePruneSizeGB(const QVariant &prune_size)
Parse pruning size value provided by user in GUI or loaded from QSettings (windows registry key or qt...
static void CopySettings(QSettings &dst, const QSettings &src)
Helper function to copy contents from one QSettings to another.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
static const bool DEFAULT_LISTEN
-listen default
Definition: net.h:75
void SetPruneTargetGB(int prune_target_gb)
QString language
Definition: optionsmodel.h:129
static const FontChoice UseBestSystemFont
Definition: optionsmodel.h:85
BitcoinUnit m_display_bitcoin_unit
Definition: optionsmodel.h:130
bool GetStartOnSystemStartup()
Definition: guiutil.cpp:663
void coinControlFeaturesChanged(bool)
bool isNum() const
Definition: univalue.h:84
std::string translated
Definition: translation.h:26
QFont fixedPitchFont(bool use_embedded_font)
Definition: guiutil.cpp:100
static int64_t PruneGBtoMiB(int gb)
Convert displayed prune target GB to configured MiB.
Definition: optionsmodel.h:34
const std::string & getValStr() const
Definition: univalue.h:68
std::variant< FontChoiceAbstract, QFont > FontChoice
Definition: optionsmodel.h:84
static QString FontChoiceToString(const OptionsModel::FontChoice &)
bool fMinimizeOnClose
Definition: optionsmodel.h:128
static int PruneSizeGB(const common::SettingsValue &prune_setting)
Get pruning size value to show in GUI from bitcoin -prune setting.
bool Init(bilingual_str &error)
bool isRestartRequired() const
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE
Default for -spendzeroconfchange.
Definition: wallet.h:126
interfaces::Node & node() const
Definition: optionsmodel.h:121
bool setOption(OptionID option, const QVariant &value, const std::string &suffix="")
static FontChoice FontChoiceFromString(const QString &)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void displayUnitChanged(BitcoinUnit unit)
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:234
std::optional< std::string > SettingToString(const common::SettingsValue &value)
Definition: args.cpp:468
virtual void mapPort(bool enable)=0
Map port.
static constexpr int DEFAULT_SCRIPTCHECK_THREADS
-par default (number of script-checking threads, 0 = auto)
bool fMinimizeToTray
Definition: optionsmodel.h:127
QVariant getOption(OptionID option, const std::string &suffix="") const
std::optional< bool > SettingToBool(const common::SettingsValue &value)
Definition: args.cpp:518
static bool PruneEnabled(const common::SettingsValue &prune_setting)
Get pruning enabled value to show in GUI from bitcoin -prune setting.
const char * DEFAULT_GUI_PROXY_HOST
static CService ip(uint32_t i)
static int PruneMiBtoGB(int64_t mib)
Convert configured prune target MiB to displayed GB.
Definition: optionsmodel.h:29
bool m_show_tray_icon
Definition: optionsmodel.h:126
static const QLatin1String fontchoice_str_best_system
bool fCoinControlFeatures
Definition: optionsmodel.h:133
bool m_enable_psbt_controls
Definition: optionsmodel.h:135
static const QString fontchoice_str_custom_prefix
Definition: messages.h:20
static constexpr uint16_t DEFAULT_GUI_PROXY_PORT
Definition: optionsmodel.h:24
ArgsManager gArgs
Definition: args.cpp:42
void checkAndMigrate()
virtual void initParameterInteraction()=0
Init parameter interaction.
static QFont getFontForChoice(const FontChoice &fc)
static constexpr bool DEFAULT_NATPMP
Definition: mapport.h:8
void setRestartRequired(bool fRequired)
std::string original
Definition: translation.h:25
static constexpr size_t DEFAULT_DB_CACHE
-dbcache default (bytes)
Definition: caches.h:18
QString strThirdPartyTxUrls
Definition: optionsmodel.h:131
QFont getFontForMoney() const
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:457
static ProxySetting ParseProxyString(const std::string &proxy)
static const QLatin1String fontchoice_str_embedded
bool m_sub_fee_from_amount
Definition: optionsmodel.h:134
bool SetStartOnSystemStartup(bool fAutoStart)
Definition: guiutil.cpp:664
std::optional< int64_t > SettingToInt(const common::SettingsValue &value)
Definition: args.cpp:493
OptionsModel(interfaces::Node &node, QObject *parent=nullptr)
void fontForMoneyChanged(const QFont &)
virtual void forceSetting(const std::string &name, const common::SettingsValue &value)=0
Force a setting value to be applied, overriding any other configuration source, but not being persist...
static QString GetDefaultProxyAddress()
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:297
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:32
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:31
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:70
void setDisplayUnit(const QVariant &new_unit)
Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal.
QString PathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:682
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QString strOverriddenByCommandLine
Definition: optionsmodel.h:139
void showTrayIconChanged(bool)