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 QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) const
400 {
401  auto setting = [&]{ return node().getPersistentSetting(SettingName(option) + suffix); };
402 
403  QSettings settings;
404  switch (option) {
405  case StartAtStartup:
407  case ShowTrayIcon:
408  return m_show_tray_icon;
409  case MinimizeToTray:
410  return fMinimizeToTray;
411  case MapPortUPnP:
412 #ifdef USE_UPNP
413  return SettingToBool(setting(), DEFAULT_UPNP);
414 #else
415  return false;
416 #endif // USE_UPNP
417  case MapPortNatpmp:
418 #ifdef USE_NATPMP
419  return SettingToBool(setting(), DEFAULT_NATPMP);
420 #else
421  return false;
422 #endif // USE_NATPMP
423  case MinimizeOnClose:
424  return fMinimizeOnClose;
425 
426  // default proxy
427  case ProxyUse:
428  case ProxyUseTor:
429  return ParseProxyString(SettingToString(setting(), "")).is_set;
430  case ProxyIP:
431  case ProxyIPTor: {
432  ProxySetting proxy = ParseProxyString(SettingToString(setting(), ""));
433  if (proxy.is_set) {
434  return proxy.ip;
435  } else if (suffix.empty()) {
436  return getOption(option, "-prev");
437  } else {
438  return ParseProxyString(GetDefaultProxyAddress().toStdString()).ip;
439  }
440  }
441  case ProxyPort:
442  case ProxyPortTor: {
443  ProxySetting proxy = ParseProxyString(SettingToString(setting(), ""));
444  if (proxy.is_set) {
445  return proxy.port;
446  } else if (suffix.empty()) {
447  return getOption(option, "-prev");
448  } else {
449  return ParseProxyString(GetDefaultProxyAddress().toStdString()).port;
450  }
451  }
452 
453 #ifdef ENABLE_WALLET
454  case SpendZeroConfChange:
456  case ExternalSignerPath:
457  return QString::fromStdString(SettingToString(setting(), ""));
458  case SubFeeFromAmount:
459  return m_sub_fee_from_amount;
460 #endif
461  case DisplayUnit:
462  return QVariant::fromValue(m_display_bitcoin_unit);
463  case ThirdPartyTxUrls:
464  return strThirdPartyTxUrls;
465  case Language:
466  return QString::fromStdString(SettingToString(setting(), ""));
467  case FontForMoney:
468  return QVariant::fromValue(m_font_money);
469  case CoinControlFeatures:
470  return fCoinControlFeatures;
471  case EnablePSBTControls:
472  return settings.value("enable_psbt_controls");
473  case Prune:
474  return PruneEnabled(setting());
475  case PruneSize:
476  return PruneEnabled(setting()) ? PruneSizeGB(setting()) :
477  suffix.empty() ? getOption(option, "-prev") :
479  case DatabaseCache:
480  return qlonglong(SettingToInt(setting(), nDefaultDbCache));
481  case ThreadsScriptVerif:
482  return qlonglong(SettingToInt(setting(), DEFAULT_SCRIPTCHECK_THREADS));
483  case Listen:
484  return SettingToBool(setting(), DEFAULT_LISTEN);
485  case Server:
486  return SettingToBool(setting(), false);
487  case MaskValues:
488  return m_mask_values;
489  default:
490  return QVariant();
491  }
492 }
493 
495 {
496  QFont f;
497  if (std::holds_alternative<FontChoiceAbstract>(fc)) {
499  f.setWeight(QFont::Bold);
500  } else {
501  f = std::get<QFont>(fc);
502  }
503  return f;
504 }
505 
507 {
509 }
510 
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:78
const std::string & getValStr() const
Definition: univalue.h:67
bool isNum() const
Definition: univalue.h:83
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:296
bool SetStartOnSystemStartup(bool fAutoStart)
Definition: guiutil.cpp:656
QString PathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:674
bool GetStartOnSystemStartup()
Definition: guiutil.cpp:655
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())