Bitcoin Core  0.19.99
P2P Digital Currency
optionsmodel.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2020 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 <interfaces/node.h>
16 #include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
17 #include <net.h>
18 #include <netbase.h>
19 #include <txdb.h> // for -dbcache defaults
20 
21 #include <QDebug>
22 #include <QSettings>
23 #include <QStringList>
24 
25 const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
26 
27 static const QString GetDefaultProxyAddress();
28 
29 OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
30  QAbstractListModel(parent), m_node(node)
31 {
32  Init(resetSettings);
33 }
34 
35 void OptionsModel::addOverriddenOption(const std::string &option)
36 {
37  strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " ";
38 }
39 
40 // Writes all missing QSettings with their default values
41 void OptionsModel::Init(bool resetSettings)
42 {
43  if (resetSettings)
44  Reset();
45 
47 
48  QSettings settings;
49 
50  // Ensure restart flag is unset on client startup
51  setRestartRequired(false);
52 
53  // These are Qt-only settings:
54 
55  // Window
56  if (!settings.contains("fHideTrayIcon"))
57  settings.setValue("fHideTrayIcon", false);
58  fHideTrayIcon = settings.value("fHideTrayIcon").toBool();
60 
61  if (!settings.contains("fMinimizeToTray"))
62  settings.setValue("fMinimizeToTray", false);
63  fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon;
64 
65  if (!settings.contains("fMinimizeOnClose"))
66  settings.setValue("fMinimizeOnClose", false);
67  fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
68 
69  // Display
70  if (!settings.contains("nDisplayUnit"))
71  settings.setValue("nDisplayUnit", BitcoinUnits::BTC);
72  nDisplayUnit = settings.value("nDisplayUnit").toInt();
73 
74  if (!settings.contains("strThirdPartyTxUrls"))
75  settings.setValue("strThirdPartyTxUrls", "");
76  strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
77 
78  if (!settings.contains("fCoinControlFeatures"))
79  settings.setValue("fCoinControlFeatures", false);
80  fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
81 
82  // These are shared with the core or have a command-line parameter
83  // and we want command-line parameters to overwrite the GUI settings.
84  //
85  // If setting doesn't exist create it with defaults.
86  //
87  // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were overridden
88  // by command-line and show this in the UI.
89 
90  // Main
91  if (!settings.contains("bPrune"))
92  settings.setValue("bPrune", false);
93  if (!settings.contains("nPruneSize"))
94  settings.setValue("nPruneSize", DEFAULT_PRUNE_TARGET_GB);
95  SetPruneEnabled(settings.value("bPrune").toBool());
96 
97  if (!settings.contains("nDatabaseCache"))
98  settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
99  if (!m_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
100  addOverriddenOption("-dbcache");
101 
102  if (!settings.contains("nThreadsScriptVerif"))
103  settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
104  if (!m_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
105  addOverriddenOption("-par");
106 
107  if (!settings.contains("strDataDir"))
108  settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
109 
110  // Wallet
111 #ifdef ENABLE_WALLET
112  if (!settings.contains("bSpendZeroConfChange"))
113  settings.setValue("bSpendZeroConfChange", true);
114  if (!m_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
115  addOverriddenOption("-spendzeroconfchange");
116 #endif
117 
118  // Network
119  if (!settings.contains("fUseUPnP"))
120  settings.setValue("fUseUPnP", DEFAULT_UPNP);
121  if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
122  addOverriddenOption("-upnp");
123 
124  if (!settings.contains("fListen"))
125  settings.setValue("fListen", DEFAULT_LISTEN);
126  if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool()))
127  addOverriddenOption("-listen");
128 
129  if (!settings.contains("fUseProxy"))
130  settings.setValue("fUseProxy", false);
131  if (!settings.contains("addrProxy"))
132  settings.setValue("addrProxy", GetDefaultProxyAddress());
133  // Only try to set -proxy, if user has enabled fUseProxy
134  if (settings.value("fUseProxy").toBool() && !m_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
135  addOverriddenOption("-proxy");
136  else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
137  addOverriddenOption("-proxy");
138 
139  if (!settings.contains("fUseSeparateProxyTor"))
140  settings.setValue("fUseSeparateProxyTor", false);
141  if (!settings.contains("addrSeparateProxyTor"))
142  settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
143  // Only try to set -onion, if user has enabled fUseSeparateProxyTor
144  if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
145  addOverriddenOption("-onion");
146  else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
147  addOverriddenOption("-onion");
148 
149  // Display
150  if (!settings.contains("language"))
151  settings.setValue("language", "");
152  if (!m_node.softSetArg("-lang", settings.value("language").toString().toStdString()))
153  addOverriddenOption("-lang");
154 
155  language = settings.value("language").toString();
156 }
157 
161 static void CopySettings(QSettings& dst, const QSettings& src)
162 {
163  for (const QString& key : src.allKeys()) {
164  dst.setValue(key, src.value(key));
165  }
166 }
167 
169 static void BackupSettings(const fs::path& filename, const QSettings& src)
170 {
171  qInfo() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
172  QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
173  dst.clear();
174  CopySettings(dst, src);
175 }
176 
178 {
179  QSettings settings;
180 
181  // Backup old settings to chain-specific datadir for troubleshooting
182  BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
183 
184  // Save the strDataDir setting
185  QString dataDir = GUIUtil::getDefaultDataDirectory();
186  dataDir = settings.value("strDataDir", dataDir).toString();
187 
188  // Remove all entries from our QSettings object
189  settings.clear();
190 
191  // Set strDataDir
192  settings.setValue("strDataDir", dataDir);
193 
194  // Set that this was reset
195  settings.setValue("fReset", true);
196 
197  // default setting for OptionsModel::StartAtStartup - disabled
200 }
201 
202 int OptionsModel::rowCount(const QModelIndex & parent) const
203 {
204  return OptionIDRowCount;
205 }
206 
207 struct ProxySetting {
208  bool is_set;
209  QString ip;
210  QString port;
211 };
212 
213 static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
214 {
215  static const ProxySetting default_val = {false, DEFAULT_GUI_PROXY_HOST, QString("%1").arg(DEFAULT_GUI_PROXY_PORT)};
216  // Handle the case that the setting is not set at all
217  if (!settings.contains(name)) {
218  return default_val;
219  }
220  // contains IP at index 0 and port at index 1
221  QStringList ip_port = settings.value(name).toString().split(":", QString::SkipEmptyParts);
222  if (ip_port.size() == 2) {
223  return {true, ip_port.at(0), ip_port.at(1)};
224  } else { // Invalid: return default
225  return default_val;
226  }
227 }
228 
229 static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
230 {
231  settings.setValue(name, ip_port.ip + ":" + ip_port.port);
232 }
233 
234 static const QString GetDefaultProxyAddress()
235 {
236  return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
237 }
238 
239 void OptionsModel::SetPruneEnabled(bool prune, bool force)
240 {
241  QSettings settings;
242  settings.setValue("bPrune", prune);
243  const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt());
244  std::string prune_val = prune ? std::to_string(prune_target_mib) : "0";
245  if (force) {
246  m_node.forceSetArg("-prune", prune_val);
247  return;
248  }
249  if (!m_node.softSetArg("-prune", prune_val)) {
250  addOverriddenOption("-prune");
251  }
252 }
253 
254 void OptionsModel::SetPruneTargetGB(int prune_target_gb, bool force)
255 {
256  const bool prune = prune_target_gb > 0;
257  if (prune) {
258  QSettings settings;
259  settings.setValue("nPruneSize", prune_target_gb);
260  }
261  SetPruneEnabled(prune, force);
262 }
263 
264 // read QSettings values and return them
265 QVariant OptionsModel::data(const QModelIndex & index, int role) const
266 {
267  if(role == Qt::EditRole)
268  {
269  QSettings settings;
270  switch(index.row())
271  {
272  case StartAtStartup:
274  case HideTrayIcon:
275  return fHideTrayIcon;
276  case MinimizeToTray:
277  return fMinimizeToTray;
278  case MapPortUPnP:
279 #ifdef USE_UPNP
280  return settings.value("fUseUPnP");
281 #else
282  return false;
283 #endif
284  case MinimizeOnClose:
285  return fMinimizeOnClose;
286 
287  // default proxy
288  case ProxyUse:
289  return settings.value("fUseProxy", false);
290  case ProxyIP:
291  return GetProxySetting(settings, "addrProxy").ip;
292  case ProxyPort:
293  return GetProxySetting(settings, "addrProxy").port;
294 
295  // separate Tor proxy
296  case ProxyUseTor:
297  return settings.value("fUseSeparateProxyTor", false);
298  case ProxyIPTor:
299  return GetProxySetting(settings, "addrSeparateProxyTor").ip;
300  case ProxyPortTor:
301  return GetProxySetting(settings, "addrSeparateProxyTor").port;
302 
303 #ifdef ENABLE_WALLET
304  case SpendZeroConfChange:
305  return settings.value("bSpendZeroConfChange");
306 #endif
307  case DisplayUnit:
308  return nDisplayUnit;
309  case ThirdPartyTxUrls:
310  return strThirdPartyTxUrls;
311  case Language:
312  return settings.value("language");
313  case CoinControlFeatures:
314  return fCoinControlFeatures;
315  case Prune:
316  return settings.value("bPrune");
317  case PruneSize:
318  return settings.value("nPruneSize");
319  case DatabaseCache:
320  return settings.value("nDatabaseCache");
321  case ThreadsScriptVerif:
322  return settings.value("nThreadsScriptVerif");
323  case Listen:
324  return settings.value("fListen");
325  default:
326  return QVariant();
327  }
328  }
329  return QVariant();
330 }
331 
332 // write QSettings values
333 bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
334 {
335  bool successful = true; /* set to false on parse error */
336  if(role == Qt::EditRole)
337  {
338  QSettings settings;
339  switch(index.row())
340  {
341  case StartAtStartup:
342  successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
343  break;
344  case HideTrayIcon:
345  fHideTrayIcon = value.toBool();
346  settings.setValue("fHideTrayIcon", fHideTrayIcon);
348  break;
349  case MinimizeToTray:
350  fMinimizeToTray = value.toBool();
351  settings.setValue("fMinimizeToTray", fMinimizeToTray);
352  break;
353  case MapPortUPnP: // core option - can be changed on-the-fly
354  settings.setValue("fUseUPnP", value.toBool());
355  m_node.mapPort(value.toBool());
356  break;
357  case MinimizeOnClose:
358  fMinimizeOnClose = value.toBool();
359  settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
360  break;
361 
362  // default proxy
363  case ProxyUse:
364  if (settings.value("fUseProxy") != value) {
365  settings.setValue("fUseProxy", value.toBool());
366  setRestartRequired(true);
367  }
368  break;
369  case ProxyIP: {
370  auto ip_port = GetProxySetting(settings, "addrProxy");
371  if (!ip_port.is_set || ip_port.ip != value.toString()) {
372  ip_port.ip = value.toString();
373  SetProxySetting(settings, "addrProxy", ip_port);
374  setRestartRequired(true);
375  }
376  }
377  break;
378  case ProxyPort: {
379  auto ip_port = GetProxySetting(settings, "addrProxy");
380  if (!ip_port.is_set || ip_port.port != value.toString()) {
381  ip_port.port = value.toString();
382  SetProxySetting(settings, "addrProxy", ip_port);
383  setRestartRequired(true);
384  }
385  }
386  break;
387 
388  // separate Tor proxy
389  case ProxyUseTor:
390  if (settings.value("fUseSeparateProxyTor") != value) {
391  settings.setValue("fUseSeparateProxyTor", value.toBool());
392  setRestartRequired(true);
393  }
394  break;
395  case ProxyIPTor: {
396  auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
397  if (!ip_port.is_set || ip_port.ip != value.toString()) {
398  ip_port.ip = value.toString();
399  SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
400  setRestartRequired(true);
401  }
402  }
403  break;
404  case ProxyPortTor: {
405  auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
406  if (!ip_port.is_set || ip_port.port != value.toString()) {
407  ip_port.port = value.toString();
408  SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
409  setRestartRequired(true);
410  }
411  }
412  break;
413 
414 #ifdef ENABLE_WALLET
415  case SpendZeroConfChange:
416  if (settings.value("bSpendZeroConfChange") != value) {
417  settings.setValue("bSpendZeroConfChange", value);
418  setRestartRequired(true);
419  }
420  break;
421 #endif
422  case DisplayUnit:
423  setDisplayUnit(value);
424  break;
425  case ThirdPartyTxUrls:
426  if (strThirdPartyTxUrls != value.toString()) {
427  strThirdPartyTxUrls = value.toString();
428  settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
429  setRestartRequired(true);
430  }
431  break;
432  case Language:
433  if (settings.value("language") != value) {
434  settings.setValue("language", value);
435  setRestartRequired(true);
436  }
437  break;
438  case CoinControlFeatures:
439  fCoinControlFeatures = value.toBool();
440  settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
442  break;
443  case Prune:
444  if (settings.value("bPrune") != value) {
445  settings.setValue("bPrune", value);
446  setRestartRequired(true);
447  }
448  break;
449  case PruneSize:
450  if (settings.value("nPruneSize") != value) {
451  settings.setValue("nPruneSize", value);
452  setRestartRequired(true);
453  }
454  break;
455  case DatabaseCache:
456  if (settings.value("nDatabaseCache") != value) {
457  settings.setValue("nDatabaseCache", value);
458  setRestartRequired(true);
459  }
460  break;
461  case ThreadsScriptVerif:
462  if (settings.value("nThreadsScriptVerif") != value) {
463  settings.setValue("nThreadsScriptVerif", value);
464  setRestartRequired(true);
465  }
466  break;
467  case Listen:
468  if (settings.value("fListen") != value) {
469  settings.setValue("fListen", value);
470  setRestartRequired(true);
471  }
472  break;
473  default:
474  break;
475  }
476  }
477 
478  Q_EMIT dataChanged(index, index);
479 
480  return successful;
481 }
482 
484 void OptionsModel::setDisplayUnit(const QVariant &value)
485 {
486  if (!value.isNull())
487  {
488  QSettings settings;
489  nDisplayUnit = value.toInt();
490  settings.setValue("nDisplayUnit", nDisplayUnit);
492  }
493 }
494 
496 {
497  QSettings settings;
498  return settings.setValue("fRestartRequired", fRequired);
499 }
500 
502 {
503  QSettings settings;
504  return settings.value("fRestartRequired", false).toBool();
505 }
506 
508 {
509  // Migration of default values
510  // Check if the QSettings container was already loaded with this client version
511  QSettings settings;
512  static const char strSettingsVersionKey[] = "nSettingsVersion";
513  int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0;
514  if (settingsVersion < CLIENT_VERSION)
515  {
516  // -dbcache was bumped from 100 to 300 in 0.13
517  // see https://github.com/bitcoin/bitcoin/pull/8273
518  // force people to upgrade to the new value if they are using 100MB
519  if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
520  settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
521 
522  settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
523  }
524 
525  // Overwrite the 'addrProxy' setting in case it has been set to an illegal
526  // default value (see issue #12623; PR #12650).
527  if (settings.contains("addrProxy") && settings.value("addrProxy").toString().endsWith("%2")) {
528  settings.setValue("addrProxy", GetDefaultProxyAddress());
529  }
530 
531  // Overwrite the 'addrSeparateProxyTor' setting in case it has been set to an illegal
532  // default value (see issue #12623; PR #12650).
533  if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) {
534  settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
535  }
536 }
static const int DEFAULT_SCRIPTCHECK_THREADS
-par default (number of script-checking threads, 0 = auto)
Definition: validation.h:82
static constexpr int DEFAULT_PRUNE_TARGET_GB
Definition: guiconstants.h:55
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
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)
void hideTrayIconChanged(bool)
NodeContext & m_node
Definition: chain.cpp:374
static void CopySettings(QSettings &dst, const QSettings &src)
Helper function to copy contents from one QSettings to another.
void SetPruneTargetGB(int prune_target_gb, bool force=false)
static const bool DEFAULT_LISTEN
-listen default
Definition: net.h:71
QString language
Definition: optionsmodel.h:102
bool GetStartOnSystemStartup()
Definition: guiutil.cpp:699
void coinControlFeaturesChanged(bool)
static int64_t PruneGBtoMiB(int gb)
Convert displayed prune target GB to configured MiB.
Definition: optionsmodel.h:28
bool fMinimizeOnClose
Definition: optionsmodel.h:101
bool isRestartRequired() const
interfaces::Node & m_node
Definition: optionsmodel.h:97
void setDisplayUnit(const QVariant &value)
Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal.
bool fHideTrayIcon
Definition: optionsmodel.h:99
void SetPruneEnabled(bool prune, bool force=false)
const char * name
Definition: rest.cpp:40
bool fMinimizeToTray
Definition: optionsmodel.h:100
const fs::path & GetDataDir(bool fNetSpecific)
Definition: system.cpp:612
const char * DEFAULT_GUI_PROXY_HOST
static const int64_t nDefaultDbCache
-dbcache default (MiB)
Definition: txdb.h:24
void Init(bool resetSettings=false)
void displayUnitChanged(int unit)
bool fCoinControlFeatures
Definition: optionsmodel.h:105
void checkAndMigrate()
virtual void mapPort(bool use_upnp)=0
Map port.
void setRestartRequired(bool fRequired)
QString strThirdPartyTxUrls
Definition: optionsmodel.h:104
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
virtual void forceSetArg(const std::string &arg, const std::string &value)=0
Set a command line argument.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:372
ArgsManager gArgs
Definition: system.cpp:76
static const bool DEFAULT_UPNP
-upnp default
Definition: net.h:76
bool SetStartOnSystemStartup(bool fAutoStart)
Definition: guiutil.cpp:700
virtual bool softSetArg(const std::string &arg, const std::string &value)=0
Set a command line argument if it doesn&#39;t already have a value.
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:253
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:715
int rowCount(const QModelIndex &parent=QModelIndex()) const
static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:39
static const QString GetDefaultProxyAddress()
static constexpr unsigned short DEFAULT_GUI_PROXY_PORT
Definition: optionsmodel.h:18
QString strOverriddenByCommandLine
Definition: optionsmodel.h:107
static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
virtual bool softSetBoolArg(const std::string &arg, bool value)=0
Set a command line boolean argument if it doesn&#39;t already have a value.
OptionsModel(interfaces::Node &node, QObject *parent=nullptr, bool resetSettings=false)