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