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