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