Bitcoin ABC  0.28.12
P2P Digital Currency
rpcconsole.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2019 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)
6 #include <config/bitcoin-config.h>
7 #endif
8 
9 #include <qt/rpcconsole.h>
10 
11 #include <chainparams.h>
12 #include <config.h>
13 #include <interfaces/node.h>
14 #include <netbase.h>
15 #include <qt/bantablemodel.h>
16 #include <qt/clientmodel.h>
17 #include <qt/forms/ui_debugwindow.h>
18 #include <qt/platformstyle.h>
19 #include <qt/walletmodel.h>
20 #include <rpc/client.h>
21 #include <rpc/server.h>
22 #include <util/strencodings.h>
23 #include <util/system.h>
24 #include <util/threadnames.h>
25 
26 #ifdef ENABLE_WALLET
27 #include <wallet/bdb.h>
28 #include <wallet/db.h>
29 #include <wallet/wallet.h>
30 #endif
31 
32 #include <univalue.h>
33 
34 #include <QFont>
35 #include <QKeyEvent>
36 #include <QMenu>
37 #include <QMessageBox>
38 #include <QScreen>
39 #include <QScrollBar>
40 #include <QSettings>
41 #include <QString>
42 #include <QStringList>
43 #include <QTime>
44 #include <QTimer>
45 
46 const int CONSOLE_HISTORY = 50;
48 const QSize FONT_RANGE(4, 40);
49 const char fontSizeSettingsKey[] = "consoleFontSize";
50 
51 const struct {
52  const char *url;
53  const char *source;
54 } ICON_MAPPING[] = {{"cmd-request", ":/icons/tx_input"},
55  {"cmd-reply", ":/icons/tx_output"},
56  {"cmd-error", ":/icons/tx_output"},
57  {"misc", ":/icons/tx_inout"},
58  {nullptr, nullptr}};
59 
60 namespace {
61 
62 // don't add private key handling cmd's to the history
63 const QStringList historyFilter = QStringList() << "importprivkey"
64  << "importmulti"
65  << "sethdseed"
66  << "signmessagewithprivkey"
67  << "signrawtransactionwithkey"
68  << "walletpassphrase"
69  << "walletpassphrasechange"
70  << "encryptwallet";
71 } // namespace
72 
73 /* Object for executing console RPC commands in a separate thread.
74  */
75 class RPCExecutor : public QObject {
76  Q_OBJECT
77 public:
79 
80 public Q_SLOTS:
81  void request(const QString &command, const WalletModel *wallet_model);
82 
83 Q_SIGNALS:
84  void reply(int category, const QString &command);
85 
86 private:
88 };
89 
93 class QtRPCTimerBase : public QObject, public RPCTimerBase {
94  Q_OBJECT
95 public:
96  QtRPCTimerBase(std::function<void()> &_func, int64_t millis) : func(_func) {
97  timer.setSingleShot(true);
98  connect(&timer, &QTimer::timeout, [this] { func(); });
99  timer.start(millis);
100  }
102 
103 private:
104  QTimer timer;
105  std::function<void()> func;
106 };
107 
109 public:
111  const char *Name() override { return "Qt"; }
112  RPCTimerBase *NewTimer(std::function<void()> &func,
113  int64_t millis) override {
114  return new QtRPCTimerBase(func, millis);
115  }
116 };
117 
118 #include <qt/rpcconsole.moc>
119 
147  std::string &strResult,
148  const std::string &strCommand,
149  const bool fExecute,
150  std::string *const pstrFilteredOut,
151  const WalletModel *wallet_model) {
152  std::vector<std::vector<std::string>> stack;
153  stack.push_back(std::vector<std::string>());
154 
155  enum CmdParseState {
156  STATE_EATING_SPACES,
157  STATE_EATING_SPACES_IN_ARG,
158  STATE_EATING_SPACES_IN_BRACKETS,
159  STATE_ARGUMENT,
160  STATE_SINGLEQUOTED,
161  STATE_DOUBLEQUOTED,
162  STATE_ESCAPE_OUTER,
163  STATE_ESCAPE_DOUBLEQUOTED,
164  STATE_COMMAND_EXECUTED,
165  STATE_COMMAND_EXECUTED_INNER
166  } state = STATE_EATING_SPACES;
167  std::string curarg;
168  UniValue lastResult;
169  unsigned nDepthInsideSensitive = 0;
170  size_t filter_begin_pos = 0, chpos;
171  std::vector<std::pair<size_t, size_t>> filter_ranges;
172 
173  auto add_to_current_stack = [&](const std::string &strArg) {
174  if (stack.back().empty() && (!nDepthInsideSensitive) &&
175  historyFilter.contains(QString::fromStdString(strArg),
176  Qt::CaseInsensitive)) {
177  nDepthInsideSensitive = 1;
178  filter_begin_pos = chpos;
179  }
180  // Make sure stack is not empty before adding something
181  if (stack.empty()) {
182  stack.push_back(std::vector<std::string>());
183  }
184  stack.back().push_back(strArg);
185  };
186 
187  auto close_out_params = [&]() {
188  if (nDepthInsideSensitive) {
189  if (!--nDepthInsideSensitive) {
190  assert(filter_begin_pos);
191  filter_ranges.push_back(
192  std::make_pair(filter_begin_pos, chpos));
193  filter_begin_pos = 0;
194  }
195  }
196  stack.pop_back();
197  };
198 
199  std::string strCommandTerminated = strCommand;
200  if (strCommandTerminated.back() != '\n') {
201  strCommandTerminated += "\n";
202  }
203  for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos) {
204  char ch = strCommandTerminated[chpos];
205  switch (state) {
206  case STATE_COMMAND_EXECUTED_INNER:
207  case STATE_COMMAND_EXECUTED: {
208  bool breakParsing = true;
209  switch (ch) {
210  case '[':
211  curarg.clear();
212  state = STATE_COMMAND_EXECUTED_INNER;
213  break;
214  default:
215  if (state == STATE_COMMAND_EXECUTED_INNER) {
216  if (ch != ']') {
217  // append char to the current argument (which is
218  // also used for the query command)
219  curarg += ch;
220  break;
221  }
222  if (curarg.size() && fExecute) {
223  // if we have a value query, query arrays with
224  // index and objects with a string key
225  UniValue subelement;
226  if (lastResult.isArray()) {
227  for (char argch : curarg) {
228  if (!IsDigit(argch)) {
229  throw std::runtime_error(
230  "Invalid result query");
231  }
232  }
233  subelement =
234  lastResult[atoi(curarg.c_str())];
235  } else if (lastResult.isObject()) {
236  subelement = lastResult.find_value(curarg);
237  } else {
238  // no array or object: abort
239  throw std::runtime_error(
240  "Invalid result query");
241  }
242  lastResult = subelement;
243  }
244 
245  state = STATE_COMMAND_EXECUTED;
246  break;
247  }
248  // don't break parsing when the char is required for the
249  // next argument
250  breakParsing = false;
251 
252  // pop the stack and return the result to the current
253  // command arguments
254  close_out_params();
255 
256  // don't stringify the json in case of a string to avoid
257  // doublequotes
258  if (lastResult.isStr()) {
259  curarg = lastResult.get_str();
260  } else {
261  curarg = lastResult.write(2);
262  }
263 
264  // if we have a non empty result, use it as stack
265  // argument otherwise as general result
266  if (curarg.size()) {
267  if (stack.size()) {
268  add_to_current_stack(curarg);
269  } else {
270  strResult = curarg;
271  }
272  }
273  curarg.clear();
274  // assume eating space state
275  state = STATE_EATING_SPACES;
276  }
277 
278  if (breakParsing) {
279  break;
280  }
281  }
282  // FALLTHROUGH
283  case STATE_ARGUMENT: // In or after argument
284  case STATE_EATING_SPACES_IN_ARG:
285  case STATE_EATING_SPACES_IN_BRACKETS:
286  case STATE_EATING_SPACES: // Handle runs of whitespace
287  switch (ch) {
288  case '"':
289  state = STATE_DOUBLEQUOTED;
290  break;
291  case '\'':
292  state = STATE_SINGLEQUOTED;
293  break;
294  case '\\':
295  state = STATE_ESCAPE_OUTER;
296  break;
297  case '(':
298  case ')':
299  case '\n':
300  if (state == STATE_EATING_SPACES_IN_ARG) {
301  throw std::runtime_error("Invalid Syntax");
302  }
303  if (state == STATE_ARGUMENT) {
304  if (ch == '(' && stack.size() &&
305  stack.back().size() > 0) {
306  if (nDepthInsideSensitive) {
307  ++nDepthInsideSensitive;
308  }
309  stack.push_back(std::vector<std::string>());
310  }
311 
312  // don't allow commands after executed commands on
313  // baselevel
314  if (!stack.size()) {
315  throw std::runtime_error("Invalid Syntax");
316  }
317 
318  add_to_current_stack(curarg);
319  curarg.clear();
320  state = STATE_EATING_SPACES_IN_BRACKETS;
321  }
322  if ((ch == ')' || ch == '\n') && stack.size() > 0) {
323  if (fExecute) {
324  // Convert argument list to JSON objects in
325  // method-dependent way, and pass it along with
326  // the method name to the dispatcher.
327  UniValue params = RPCConvertValues(
328  stack.back()[0],
329  std::vector<std::string>(
330  stack.back().begin() + 1,
331  stack.back().end()));
332  std::string method = stack.back()[0];
333  std::string uri;
334 
335 #ifdef ENABLE_WALLET
336  if (wallet_model) {
337  QByteArray encodedName =
338  QUrl::toPercentEncoding(
339  wallet_model->getWalletName());
340  uri = "/wallet/" +
341  std::string(encodedName.constData(),
342  encodedName.length());
343  }
344 #endif
345 
346  assert(node);
347  lastResult = node->executeRpc(
348  GetConfig(), method, params, uri);
349  }
350 
351  state = STATE_COMMAND_EXECUTED;
352  curarg.clear();
353  }
354  break;
355  case ' ':
356  case ',':
357  case '\t':
358  if (state == STATE_EATING_SPACES_IN_ARG &&
359  curarg.empty() && ch == ',') {
360  throw std::runtime_error("Invalid Syntax");
361  } else if (state == STATE_ARGUMENT) {
362  // Space ends argument
363  add_to_current_stack(curarg);
364  curarg.clear();
365  }
366  if ((state == STATE_EATING_SPACES_IN_BRACKETS ||
367  state == STATE_ARGUMENT) &&
368  ch == ',') {
369  state = STATE_EATING_SPACES_IN_ARG;
370  break;
371  }
372  state = STATE_EATING_SPACES;
373  break;
374  default:
375  curarg += ch;
376  state = STATE_ARGUMENT;
377  }
378  break;
379  case STATE_SINGLEQUOTED: // Single-quoted string
380  switch (ch) {
381  case '\'':
382  state = STATE_ARGUMENT;
383  break;
384  default:
385  curarg += ch;
386  }
387  break;
388  case STATE_DOUBLEQUOTED: // Double-quoted string
389  switch (ch) {
390  case '"':
391  state = STATE_ARGUMENT;
392  break;
393  case '\\':
394  state = STATE_ESCAPE_DOUBLEQUOTED;
395  break;
396  default:
397  curarg += ch;
398  }
399  break;
400  case STATE_ESCAPE_OUTER: // '\' outside quotes
401  curarg += ch;
402  state = STATE_ARGUMENT;
403  break;
404  case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
405  if (ch != '"' && ch != '\\') {
406  // keep '\' for everything but the quote and '\' itself
407  curarg += '\\';
408  }
409  curarg += ch;
410  state = STATE_DOUBLEQUOTED;
411  break;
412  }
413  }
414  if (pstrFilteredOut) {
415  if (STATE_COMMAND_EXECUTED == state) {
416  assert(!stack.empty());
417  close_out_params();
418  }
419  *pstrFilteredOut = strCommand;
420  for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) {
421  pstrFilteredOut->replace(i->first, i->second - i->first, "(…)");
422  }
423  }
424 
425  // final state
426  switch (state) {
427  case STATE_COMMAND_EXECUTED:
428  if (lastResult.isStr()) {
429  strResult = lastResult.get_str();
430  } else {
431  strResult = lastResult.write(2);
432  }
433  // FALLTHROUGH
434  case STATE_ARGUMENT:
435  case STATE_EATING_SPACES:
436  return true;
437  default: // ERROR to end in one of the other states
438  return false;
439  }
440 }
441 
442 void RPCExecutor::request(const QString &command,
443  const WalletModel *wallet_model) {
444  try {
445  std::string result;
446  std::string executableCommand = command.toStdString() + "\n";
447 
448  // Catch the console-only-help command before RPC call is executed and
449  // reply with help text as-if a RPC reply.
450  if (executableCommand == "help-console\n") {
451  Q_EMIT reply(
453  QString(("\n"
454  "This console accepts RPC commands using the standard "
455  "syntax.\n"
456  " example: getblockhash 0\n\n"
457 
458  "This console can also accept RPC commands using "
459  "parenthesized syntax.\n"
460  " example: getblockhash(0)\n\n"
461 
462  "Commands may be nested when specified with the "
463  "parenthesized syntax.\n"
464  " example: getblock(getblockhash(0) 1)\n\n"
465 
466  "A space or a comma can be used to delimit arguments "
467  "for either syntax.\n"
468  " example: getblockhash 0\n"
469  " getblockhash,0\n\n"
470 
471  "Named results can be queried with a non-quoted key "
472  "string in brackets.\n"
473  " example: getblock(getblockhash(0) true)[tx]\n\n"
474 
475  "Results without keys can be queried using an integer "
476  "in brackets.\n"
477  " example: "
478  "getblock(getblockhash(0),true)[tx][0]\n\n")));
479  return;
480  }
482  m_node, result, executableCommand, nullptr, wallet_model)) {
484  QString("Parse error: unbalanced ' or \""));
485  return;
486  }
487 
488  Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
489  } catch (UniValue &objError) {
490  // Nice formatting for standard-format error
491  try {
492  int code = objError.find_value("code").get_int();
493  std::string message = objError.find_value("message").get_str();
495  QString::fromStdString(message) + " (code " +
496  QString::number(code) + ")");
497  } catch (const std::runtime_error &) {
498  // raised when converting to invalid type, i.e. missing code or
499  // message. Show raw JSON object.
501  QString::fromStdString(objError.write()));
502  }
503  } catch (const std::exception &e) {
505  QString("Error: ") + QString::fromStdString(e.what()));
506  }
507 }
508 
510  const PlatformStyle *_platformStyle, QWidget *parent)
511  : QWidget(parent), m_node(node), ui(new Ui::RPCConsole),
512  platformStyle(_platformStyle) {
513  ui->setupUi(this);
514  QSettings settings;
515  if (!restoreGeometry(
516  settings.value("RPCConsoleWindowGeometry").toByteArray())) {
517  // Restore failed (perhaps missing setting), center the window
518  move(QGuiApplication::primaryScreen()->availableGeometry().center() -
519  frameGeometry().center());
520  }
521 
522  QChar nonbreaking_hyphen(8209);
523  const QString hb_list{"<ul><li>\"" + tr("To") + "\" – " +
524  tr("we selected the peer for high bandwidth relay") +
525  "</li><li>\"" + tr("From") + "\" – " +
526  tr("the peer selected us for high bandwidth relay") +
527  "</li><li>\"" + tr("No") + "\" – " +
528  tr("no high bandwidth relay selected") +
529  "</li></ul>"};
530  ui->peerHighBandwidthLabel->setToolTip(
531  ui->peerHighBandwidthLabel->toolTip().arg(hb_list));
532  ui->dataDir->setToolTip(
533  ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
534  ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(
535  QString(nonbreaking_hyphen) + "blocksdir"));
536  ui->openDebugLogfileButton->setToolTip(
537  ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME));
538 
540  ui->openDebugLogfileButton->setIcon(
541  platformStyle->SingleColorIcon(":/icons/export"));
542  }
543  ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
544  ui->fontBiggerButton->setIcon(
545  platformStyle->SingleColorIcon(":/icons/fontbigger"));
546  ui->fontSmallerButton->setIcon(
547  platformStyle->SingleColorIcon(":/icons/fontsmaller"));
548 
549  // Install event filter for up and down arrow
550  ui->lineEdit->installEventFilter(this);
551  ui->lineEdit->setMaxLength(16 * 1024 * 1024);
552  ui->messagesWidget->installEventFilter(this);
553 
554  connect(ui->clearButton, &QPushButton::clicked, this, &RPCConsole::clear);
555  connect(ui->fontBiggerButton, &QPushButton::clicked, this,
557  connect(ui->fontSmallerButton, &QPushButton::clicked, this,
559  connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph,
561 
562  // disable the wallet selector by default
563  ui->WalletSelector->setVisible(false);
564  ui->WalletSelectorLabel->setVisible(false);
565 
566 // set library version labels
567 #ifdef ENABLE_WALLET
568  ui->berkeleyDBVersion->setText(
569  QString::fromStdString(BerkeleyDatabaseVersion()));
570 #else
571  ui->label_berkeleyDBVersion->hide();
572  ui->berkeleyDBVersion->hide();
573 #endif
574  // Register RPC timer interface
576  // avoid accidentally overwriting an existing, non QTThread
577  // based timer interface
579 
582 
584  settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt();
585  clear();
586 
588 }
589 
591  QSettings settings;
592  settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
594  delete rpcTimerInterface;
595  delete ui;
596 }
597 
598 bool RPCConsole::eventFilter(QObject *obj, QEvent *event) {
599  // Special key handling
600  if (event->type() == QEvent::KeyPress) {
601  QKeyEvent *keyevt = static_cast<QKeyEvent *>(event);
602  int key = keyevt->key();
603  Qt::KeyboardModifiers mod = keyevt->modifiers();
604  switch (key) {
605  case Qt::Key_Up:
606  if (obj == ui->lineEdit) {
607  browseHistory(-1);
608  return true;
609  }
610  break;
611  case Qt::Key_Down:
612  if (obj == ui->lineEdit) {
613  browseHistory(1);
614  return true;
615  }
616  break;
617  case Qt::Key_PageUp: /* pass paging keys to messages widget */
618  case Qt::Key_PageDown:
619  if (obj == ui->lineEdit) {
620  QApplication::postEvent(ui->messagesWidget,
621  new QKeyEvent(*keyevt));
622  return true;
623  }
624  break;
625  case Qt::Key_Return:
626  case Qt::Key_Enter:
627  // forward these events to lineEdit
628  if (obj == autoCompleter->popup()) {
629  QApplication::postEvent(ui->lineEdit,
630  new QKeyEvent(*keyevt));
631  return true;
632  }
633  break;
634  default:
635  // Typing in messages widget brings focus to line edit, and
636  // redirects key there. Exclude most combinations and keys that
637  // emit no text, except paste shortcuts.
638  if (obj == ui->messagesWidget &&
639  ((!mod && !keyevt->text().isEmpty() &&
640  key != Qt::Key_Tab) ||
641  ((mod & Qt::ControlModifier) && key == Qt::Key_V) ||
642  ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert))) {
643  ui->lineEdit->setFocus();
644  QApplication::postEvent(ui->lineEdit,
645  new QKeyEvent(*keyevt));
646  return true;
647  }
648  }
649  }
650  return QWidget::eventFilter(obj, event);
651 }
652 
653 void RPCConsole::setClientModel(ClientModel *model, int bestblock_height,
654  int64_t bestblock_date,
655  double verification_progress) {
656  clientModel = model;
657 
658  bool wallet_enabled{false};
659 #ifdef ENABLE_WALLET
660  wallet_enabled = WalletModel::isWalletEnabled();
661 #endif // ENABLE_WALLET
662  if (model && !wallet_enabled) {
663  // Show warning, for example if this is a prerelease version
664  connect(model, &ClientModel::alertsChanged, this,
667  }
668 
669  ui->trafficGraph->setClientModel(model);
670  if (model && clientModel->getPeerTableModel() &&
672  // Keep up to date with client
674  connect(model, &ClientModel::numConnectionsChanged, this,
676 
677  setNumBlocks(bestblock_height, QDateTime::fromTime_t(bestblock_date),
678  verification_progress, SyncType::BLOCK_SYNC);
679  connect(model, &ClientModel::numBlocksChanged, this,
681 
683  connect(model, &ClientModel::networkActiveChanged, this,
685 
687  updateTrafficStats(node.getTotalBytesRecv(), node.getTotalBytesSent());
688  connect(model, &ClientModel::bytesChanged, this,
690 
691  connect(model, &ClientModel::mempoolSizeChanged, this,
693 
694  // set up peer table
695  ui->peerWidget->setModel(model->getPeerTableModel());
696  ui->peerWidget->verticalHeader()->hide();
697  ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
698  ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
699  ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
700  ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
701  ui->peerWidget->setColumnWidth(PeerTableModel::Address,
703  ui->peerWidget->setColumnWidth(PeerTableModel::Subversion,
705  ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
706  ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
707 
708  // create peer table context menu actions
709  QAction *disconnectAction = new QAction(tr("&Disconnect"), this);
710  QAction *banAction1h =
711  new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
712  QAction *banAction24h =
713  new QAction(tr("Ban for") + " " + tr("1 &day"), this);
714  QAction *banAction7d =
715  new QAction(tr("Ban for") + " " + tr("1 &week"), this);
716  QAction *banAction365d =
717  new QAction(tr("Ban for") + " " + tr("1 &year"), this);
718 
719  // create peer table context menu
720  peersTableContextMenu = new QMenu(this);
721  peersTableContextMenu->addAction(disconnectAction);
722  peersTableContextMenu->addAction(banAction1h);
723  peersTableContextMenu->addAction(banAction24h);
724  peersTableContextMenu->addAction(banAction7d);
725  peersTableContextMenu->addAction(banAction365d);
726 
727  connect(banAction1h, &QAction::triggered,
728  [this] { banSelectedNode(60 * 60); });
729  connect(banAction24h, &QAction::triggered,
730  [this] { banSelectedNode(60 * 60 * 24); });
731  connect(banAction7d, &QAction::triggered,
732  [this] { banSelectedNode(60 * 60 * 24 * 7); });
733  connect(banAction365d, &QAction::triggered,
734  [this] { banSelectedNode(60 * 60 * 24 * 365); });
735 
736  // peer table context menu signals
737  connect(ui->peerWidget, &QTableView::customContextMenuRequested, this,
739  connect(disconnectAction, &QAction::triggered, this,
741 
742  // peer table signal handling - update peer details when selecting new
743  // node
744  connect(ui->peerWidget->selectionModel(),
745  &QItemSelectionModel::selectionChanged, this,
747  // peer table signal handling - update peer details when new nodes are
748  // added to the model
749  connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged,
751  // peer table signal handling - cache selected node ids
752  connect(model->getPeerTableModel(),
753  &PeerTableModel::layoutAboutToBeChanged, this,
755 
756  // set up ban table
757  ui->banlistWidget->setModel(model->getBanTableModel());
758  ui->banlistWidget->verticalHeader()->hide();
759  ui->banlistWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
760  ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
761  ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
762  ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
763  ui->banlistWidget->setColumnWidth(BanTableModel::Address,
765  ui->banlistWidget->setColumnWidth(BanTableModel::Bantime,
767  ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
768 
769  // create ban table context menu action
770  QAction *unbanAction = new QAction(tr("&Unban"), this);
771 
772  // create ban table context menu
773  banTableContextMenu = new QMenu(this);
774  banTableContextMenu->addAction(unbanAction);
775 
776  // ban table context menu signals
777  connect(ui->banlistWidget, &QTableView::customContextMenuRequested,
779  connect(unbanAction, &QAction::triggered, this,
781 
782  // ban table signal handling - clear peer details when clicking a peer
783  // in the ban table
784  connect(ui->banlistWidget, &QTableView::clicked, this,
786  // ban table signal handling - ensure ban table is shown or hidden (if
787  // empty)
788  connect(model->getBanTableModel(), &BanTableModel::layoutChanged, this,
791 
792  // Provide initial values
793  ui->clientVersion->setText(model->formatFullVersion());
794  ui->clientUserAgent->setText(model->formatSubVersion());
795  ui->dataDir->setText(model->dataDir());
796  ui->blocksDir->setText(model->blocksDir());
797  ui->startupTime->setText(model->formatClientStartupTime());
798  ui->networkName->setText(
799  QString::fromStdString(Params().NetworkIDString()));
800 
801  // Setup autocomplete and attach it
802  QStringList wordList;
803  std::vector<std::string> commandList = m_node.listRpcCommands();
804  for (size_t i = 0; i < commandList.size(); ++i) {
805  wordList << commandList[i].c_str();
806  wordList << ("help " + commandList[i]).c_str();
807  }
808 
809  wordList << "help-console";
810  wordList.sort();
811  autoCompleter = new QCompleter(wordList, this);
812  autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
813  // ui->lineEdit is initially disabled because running commands is only
814  // possible from now on.
815  ui->lineEdit->setEnabled(true);
816  ui->lineEdit->setCompleter(autoCompleter);
817  autoCompleter->popup()->installEventFilter(this);
818  // Start thread to execute RPC commands.
819  startExecutor();
820  }
821  if (!model) {
822  // Client model is being set to 0, this means shutdown() is about to be
823  // called.
824  thread.quit();
825  thread.wait();
826  }
827 }
828 
829 #ifdef ENABLE_WALLET
830 void RPCConsole::addWallet(WalletModel *const walletModel) {
831  // use name for text and wallet model for internal data object (to allow to
832  // move to a wallet id later)
833  ui->WalletSelector->addItem(walletModel->getDisplayName(),
834  QVariant::fromValue(walletModel));
835  if (ui->WalletSelector->count() == 2 && !isVisible()) {
836  // First wallet added, set to default so long as the window isn't
837  // presently visible (and potentially in use)
838  ui->WalletSelector->setCurrentIndex(1);
839  }
840  if (ui->WalletSelector->count() > 2) {
841  ui->WalletSelector->setVisible(true);
842  ui->WalletSelectorLabel->setVisible(true);
843  }
844 }
845 
846 void RPCConsole::removeWallet(WalletModel *const walletModel) {
847  ui->WalletSelector->removeItem(
848  ui->WalletSelector->findData(QVariant::fromValue(walletModel)));
849  if (ui->WalletSelector->count() == 2) {
850  ui->WalletSelector->setVisible(false);
851  ui->WalletSelectorLabel->setVisible(false);
852  }
853 }
854 #endif
855 
856 static QString categoryClass(int category) {
857  switch (category) {
859  return "cmd-request";
860  break;
862  return "cmd-reply";
863  break;
865  return "cmd-error";
866  break;
867  default:
868  return "misc";
869  }
870 }
871 
874 }
875 
878 }
879 
880 void RPCConsole::setFontSize(int newSize) {
881  QSettings settings;
882 
883  // don't allow an insane font size
884  if (newSize < FONT_RANGE.width() || newSize > FONT_RANGE.height()) {
885  return;
886  }
887 
888  // temp. store the console content
889  QString str = ui->messagesWidget->toHtml();
890 
891  // replace font tags size in current content
892  str.replace(QString("font-size:%1pt").arg(consoleFontSize),
893  QString("font-size:%1pt").arg(newSize));
894 
895  // store the new font size
896  consoleFontSize = newSize;
897  settings.setValue(fontSizeSettingsKey, consoleFontSize);
898 
899  // clear console (reset icon sizes, default stylesheet) and re-add the
900  // content
901  float oldPosFactor = 1.0 /
902  ui->messagesWidget->verticalScrollBar()->maximum() *
903  ui->messagesWidget->verticalScrollBar()->value();
904  clear(false);
905  ui->messagesWidget->setHtml(str);
906  ui->messagesWidget->verticalScrollBar()->setValue(
907  oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
908 }
909 
910 void RPCConsole::clear(bool clearHistory) {
911  ui->messagesWidget->clear();
912  if (clearHistory) {
913  history.clear();
914  historyPtr = 0;
915  }
916  ui->lineEdit->clear();
917  ui->lineEdit->setFocus();
918 
919  // Add smoothly scaled icon images.
920  // (when using width/height on an img, Qt uses nearest instead of linear
921  // interpolation)
922  for (int i = 0; ICON_MAPPING[i].url; ++i) {
923  ui->messagesWidget->document()->addResource(
924  QTextDocument::ImageResource, QUrl(ICON_MAPPING[i].url),
926  .scaled(QSize(consoleFontSize * 2, consoleFontSize * 2),
927  Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
928  }
929 
930  // Set default style sheet
931  QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont());
932  ui->messagesWidget->document()->setDefaultStyleSheet(
933  QString("table { }"
934  "td.time { color: #808080; font-size: %2; padding-top: 3px; } "
935  "td.message { font-family: %1; font-size: %2; "
936  "white-space:pre-wrap; } "
937  "td.cmd-request { color: #006060; } "
938  "td.cmd-error { color: red; } "
939  ".secwarning { color: red; }"
940  "b { color: #006060; } ")
941  .arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize)));
942 
943 #ifdef Q_OS_MAC
944  QString clsKey = "(⌘)-L";
945 #else
946  QString clsKey = "Ctrl-L";
947 #endif
948 
950  (tr("Welcome to the %1 RPC console.").arg(PACKAGE_NAME) + "<br>" +
951  tr("Use up and down arrows to navigate history, and "
952  "%1 to clear screen.")
953  .arg("<b>" + clsKey + "</b>") +
954  "<br>" +
955  tr("Type %1 for an overview of available commands.")
956  .arg("<b>help</b>") +
957  "<br>" +
958  tr("For more information on using this console type %1.")
959  .arg("<b>help-console</b>") +
960  "<br><span class=\"secwarning\"><br>" +
961  tr("WARNING: Scammers have been active, telling users to type "
962  "commands here, stealing their wallet contents. Do not use "
963  "this console without fully understanding the ramifications "
964  "of a command.") +
965  "</span>"),
966  true);
967 }
968 
969 void RPCConsole::keyPressEvent(QKeyEvent *event) {
970  if (windowType() != Qt::Widget && event->key() == Qt::Key_Escape) {
971  close();
972  }
973 }
974 
975 void RPCConsole::message(int category, const QString &message, bool html) {
976  QTime time = QTime::currentTime();
977  QString timeString = time.toString();
978  QString out;
979  out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
980  out += "<td class=\"icon\" width=\"32\"><img src=\"" +
981  categoryClass(category) + "\"></td>";
982  out += "<td class=\"message " + categoryClass(category) +
983  "\" valign=\"middle\">";
984  if (html) {
985  out += message;
986  } else {
987  out += GUIUtil::HtmlEscape(message, false);
988  }
989  out += "</td></tr></table>";
990  ui->messagesWidget->append(out);
991 }
992 
994  QString connections =
995  QString::number(clientModel->getNumConnections()) + " (";
996  connections += tr("In:") + " " +
997  QString::number(clientModel->getNumConnections(
999  " / ";
1000  connections += tr("Out:") + " " +
1001  QString::number(clientModel->getNumConnections(
1003  ")";
1004 
1005  if (!clientModel->node().getNetworkActive()) {
1006  connections += " (" + tr("Network activity disabled") + ")";
1007  }
1008 
1009  ui->numberOfConnections->setText(connections);
1010 }
1011 
1013  if (!clientModel) {
1014  return;
1015  }
1016 
1018 }
1019 
1020 void RPCConsole::setNetworkActive(bool networkActive) {
1022 }
1023 
1024 void RPCConsole::setNumBlocks(int count, const QDateTime &blockDate,
1025  double nVerificationProgress, SyncType synctype) {
1026  if (synctype == SyncType::BLOCK_SYNC) {
1027  ui->numberOfBlocks->setText(QString::number(count));
1028  ui->lastBlockTime->setText(blockDate.toString());
1029  }
1030 }
1031 
1032 void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage) {
1033  ui->mempoolNumberTxs->setText(QString::number(numberOfTxs));
1034 
1035  if (dynUsage < 1000000) {
1036  ui->mempoolSize->setText(QString::number(dynUsage / 1000.0, 'f', 2) +
1037  " KB");
1038  } else {
1039  ui->mempoolSize->setText(QString::number(dynUsage / 1000000.0, 'f', 2) +
1040  " MB");
1041  }
1042 }
1043 
1045  QString cmd = ui->lineEdit->text();
1046 
1047  if (!cmd.isEmpty()) {
1048  std::string strFilteredCmd;
1049  try {
1050  std::string dummy;
1051  if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false,
1052  &strFilteredCmd)) {
1053  // Failed to parse command, so we cannot even filter it for the
1054  // history
1055  throw std::runtime_error("Invalid command line");
1056  }
1057  } catch (const std::exception &e) {
1058  QMessageBox::critical(this, "Error",
1059  QString("Error: ") +
1060  QString::fromStdString(e.what()));
1061  return;
1062  }
1063 
1064  ui->lineEdit->clear();
1065 
1066  cmdBeforeBrowsing = QString();
1067 
1068 #ifdef ENABLE_WALLET
1069  WalletModel *wallet_model =
1070  ui->WalletSelector->currentData().value<WalletModel *>();
1071 
1072  if (m_last_wallet_model != wallet_model) {
1073  if (wallet_model) {
1074  message(CMD_REQUEST, tr("Executing command using \"%1\" wallet")
1075  .arg(wallet_model->getWalletName()));
1076  } else {
1078  tr("Executing command without any wallet"));
1079  }
1080  m_last_wallet_model = wallet_model;
1081  }
1082 #endif
1083 
1084  message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
1085  Q_EMIT cmdRequest(cmd, m_last_wallet_model);
1086 
1087  cmd = QString::fromStdString(strFilteredCmd);
1088 
1089  // Remove command, if already in history
1090  history.removeOne(cmd);
1091  // Append command to history
1092  history.append(cmd);
1093  // Enforce maximum history size
1094  while (history.size() > CONSOLE_HISTORY) {
1095  history.removeFirst();
1096  }
1097  // Set pointer to end of history
1098  historyPtr = history.size();
1099 
1100  // Scroll console view to end
1101  scrollToEnd();
1102  }
1103 }
1104 
1105 void RPCConsole::browseHistory(int offset) {
1106  // store current text when start browsing through the history
1107  if (historyPtr == history.size()) {
1108  cmdBeforeBrowsing = ui->lineEdit->text();
1109  }
1110 
1111  historyPtr += offset;
1112  if (historyPtr < 0) {
1113  historyPtr = 0;
1114  }
1115  if (historyPtr > history.size()) {
1116  historyPtr = history.size();
1117  }
1118  QString cmd;
1119  if (historyPtr < history.size()) {
1120  cmd = history.at(historyPtr);
1121  } else if (!cmdBeforeBrowsing.isNull()) {
1122  cmd = cmdBeforeBrowsing;
1123  }
1124  ui->lineEdit->setText(cmd);
1125 }
1126 
1128  RPCExecutor *executor = new RPCExecutor(m_node);
1129  executor->moveToThread(&thread);
1130 
1131  // Replies from executor object must go to this object
1132  connect(executor, &RPCExecutor::reply, this,
1133  static_cast<void (RPCConsole::*)(int, const QString &)>(
1135 
1136  // Requests from this object must go to executor
1137  connect(this, &RPCConsole::cmdRequest, executor, &RPCExecutor::request);
1138 
1139  // Make sure executor object is deleted in its own thread
1140  connect(&thread, &QThread::finished, executor, &RPCExecutor::deleteLater);
1141 
1142  // Default implementation of QThread::run() simply spins up an event loop in
1143  // the thread, which is what we want.
1144  thread.start();
1145  QTimer::singleShot(0, executor,
1146  []() { util::ThreadRename("qt-rpcconsole"); });
1147 }
1148 
1150  if (ui->tabWidget->widget(index) == ui->tab_console) {
1151  ui->lineEdit->setFocus();
1152  }
1153 }
1154 
1157 }
1158 
1160  QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
1161  scrollbar->setValue(scrollbar->maximum());
1162 }
1163 
1165  const int multiplier = 5; // each position on the slider represents 5 min
1166  int mins = value * multiplier;
1167  setTrafficGraphRange(mins);
1168 }
1169 
1171  ui->trafficGraph->setGraphRangeMins(mins);
1172  ui->lblGraphRange->setText(
1173  GUIUtil::formatDurationStr(std::chrono::minutes{mins}));
1174 }
1175 
1176 void RPCConsole::updateTrafficStats(quint64 totalBytesIn,
1177  quint64 totalBytesOut) {
1178  ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn));
1179  ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
1180 }
1181 
1183  QModelIndexList selected =
1184  ui->peerWidget->selectionModel()->selectedIndexes();
1185  cachedNodeids.clear();
1186  for (int i = 0; i < selected.size(); i++) {
1187  const CNodeCombinedStats *stats =
1189  selected.at(i).row());
1190  cachedNodeids.append(stats->nodeStats.nodeid);
1191  }
1192 }
1193 
1196  return;
1197  }
1198 
1199  bool fUnselect = false;
1200  bool fReselect = false;
1201 
1202  // no node selected yet
1203  if (cachedNodeids.empty()) {
1204  return;
1205  }
1206 
1207  // find the currently selected row
1208  int selectedRow = -1;
1209  QModelIndexList selectedModelIndex =
1210  ui->peerWidget->selectionModel()->selectedIndexes();
1211  if (!selectedModelIndex.isEmpty()) {
1212  selectedRow = selectedModelIndex.first().row();
1213  }
1214 
1215  // check if our detail node has a row in the table (it may not necessarily
1216  // be at selectedRow since its position can change after a layout change)
1217  int detailNodeRow =
1219 
1220  if (detailNodeRow < 0) {
1221  // detail node disappeared from table (node disconnected)
1222  fUnselect = true;
1223  } else {
1224  if (detailNodeRow != selectedRow) {
1225  // detail node moved position
1226  fUnselect = true;
1227  fReselect = true;
1228  }
1229  }
1230 
1231  if (fUnselect && selectedRow >= 0) {
1233  }
1234 
1235  if (fReselect) {
1236  for (int i = 0; i < cachedNodeids.size(); i++) {
1237  ui->peerWidget->selectRow(
1239  cachedNodeids.at(i)));
1240  }
1241  }
1242 
1244 }
1245 
1247  QModelIndexList selected_rows;
1248  auto selection_model = ui->peerWidget->selectionModel();
1249  if (selection_model) {
1250  selected_rows = selection_model->selectedRows();
1251  }
1253  selected_rows.size() != 1) {
1254  ui->detailWidget->hide();
1255  ui->peerHeading->setText(
1256  tr("Select a peer to view detailed information."));
1257  return;
1258  }
1259  const CNodeCombinedStats *stats =
1261  selected_rows.first().row());
1262  // update the detail ui with latest node information
1263  QString peerAddrDetails(
1264  QString::fromStdString(stats->nodeStats.m_addr_name) + " ");
1265  peerAddrDetails +=
1266  tr("(peer id: %1)").arg(QString::number(stats->nodeStats.nodeid));
1267  if (!stats->nodeStats.addrLocal.empty()) {
1268  peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(
1269  stats->nodeStats.addrLocal));
1270  }
1271  ui->peerHeading->setText(peerAddrDetails);
1272  QString bip152_hb_settings;
1273  if (stats->nodeStats.m_bip152_highbandwidth_to) {
1274  bip152_hb_settings += "To";
1275  }
1277  bip152_hb_settings += (bip152_hb_settings == "" ? "From" : "/From");
1278  }
1279  if (bip152_hb_settings == "") {
1280  bip152_hb_settings = "No";
1281  }
1282  ui->peerHighBandwidth->setText(bip152_hb_settings);
1283  const auto time_now{GetTime<std::chrono::seconds>()};
1284  ui->peerConnTime->setText(
1285  GUIUtil::formatDurationStr(time_now - stats->nodeStats.m_connected));
1286  ui->peerLastBlock->setText(
1287  TimeDurationField(time_now, stats->nodeStats.m_last_block_time));
1288  ui->peerLastTx->setText(
1289  TimeDurationField(time_now, stats->nodeStats.m_last_tx_time));
1290  ui->peerLastSend->setText(
1291  TimeDurationField(time_now, stats->nodeStats.m_last_send));
1292  ui->peerLastRecv->setText(
1293  TimeDurationField(time_now, stats->nodeStats.m_last_recv));
1294  ui->peerBytesSent->setText(
1296  ui->peerBytesRecv->setText(
1298  ui->peerPingTime->setText(
1300  ui->peerMinPing->setText(
1302  ui->timeoffset->setText(
1304  ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
1305  ui->peerSubversion->setText(
1306  QString::fromStdString(stats->nodeStats.cleanSubVer));
1307  ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound")
1308  : tr("Outbound"));
1309  ui->peerNetwork->setText(
1312  ui->peerPermissions->setText(tr("N/A"));
1313  } else {
1314  QStringList permissions;
1315  for (const auto &permission :
1317  permissions.append(QString::fromStdString(permission));
1318  }
1319  ui->peerPermissions->setText(permissions.join(" & "));
1320  }
1321  ui->peerMappedAS->setText(
1322  stats->nodeStats.m_mapped_as != 0
1323  ? QString::number(stats->nodeStats.m_mapped_as)
1324  : tr("N/A"));
1325 
1326  // This check fails for example if the lock was busy and
1327  // nodeStateStats couldn't be fetched.
1328  if (stats->fNodeStateStatsAvailable) {
1329  ui->peerServices->setText(
1331  // Sync height is init to -1
1332  if (stats->nodeStateStats.nSyncHeight > -1) {
1333  ui->peerSyncHeight->setText(
1334  QString("%1").arg(stats->nodeStateStats.nSyncHeight));
1335  } else {
1336  ui->peerSyncHeight->setText(tr("Unknown"));
1337  }
1338 
1339  // Common height is init to -1
1340  if (stats->nodeStateStats.nCommonHeight > -1) {
1341  ui->peerCommonHeight->setText(
1342  QString("%1").arg(stats->nodeStateStats.nCommonHeight));
1343  } else {
1344  ui->peerCommonHeight->setText(tr("Unknown"));
1345  }
1346 
1347  ui->peerHeight->setText(
1348  QString::number(stats->nodeStateStats.m_starting_height));
1349  ui->peerPingWait->setText(
1351  ui->peerRelayTxes->setText(stats->nodeStateStats.m_relay_txs ? "Yes"
1352  : "No");
1353  }
1354 
1355  ui->detailWidget->show();
1356 }
1357 
1358 void RPCConsole::resizeEvent(QResizeEvent *event) {
1359  QWidget::resizeEvent(event);
1360 }
1361 
1362 void RPCConsole::showEvent(QShowEvent *event) {
1363  QWidget::showEvent(event);
1364 
1366  return;
1367  }
1368 
1369  // start PeerTableModel auto refresh
1371 }
1372 
1373 void RPCConsole::hideEvent(QHideEvent *event) {
1374  QWidget::hideEvent(event);
1375 
1377  return;
1378  }
1379 
1380  // stop PeerTableModel auto refresh
1382 }
1383 
1384 void RPCConsole::showPeersTableContextMenu(const QPoint &point) {
1385  QModelIndex index = ui->peerWidget->indexAt(point);
1386  if (index.isValid()) {
1387  peersTableContextMenu->exec(QCursor::pos());
1388  }
1389 }
1390 
1391 void RPCConsole::showBanTableContextMenu(const QPoint &point) {
1392  QModelIndex index = ui->banlistWidget->indexAt(point);
1393  if (index.isValid()) {
1394  banTableContextMenu->exec(QCursor::pos());
1395  }
1396 }
1397 
1399  // Get selected peer addresses
1400  QList<QModelIndex> nodes =
1402  for (int i = 0; i < nodes.count(); i++) {
1403  // Get currently selected peer address
1404  NodeId id = nodes.at(i).data().toLongLong();
1405  // Find the node, disconnect it and clear the selected node
1406  if (m_node.disconnectById(id)) {
1408  }
1409  }
1410 }
1411 
1412 void RPCConsole::banSelectedNode(int bantime) {
1413  if (!clientModel) {
1414  return;
1415  }
1416 
1417  // Get selected peer addresses
1418  QList<QModelIndex> nodes =
1420  for (int i = 0; i < nodes.count(); i++) {
1421  // Get currently selected peer address
1422  NodeId id = nodes.at(i).data().toLongLong();
1423 
1424  // Get currently selected peer address
1425  int detailNodeRow =
1427  if (detailNodeRow < 0) {
1428  return;
1429  }
1430 
1431  // Find possible nodes, ban it and clear the selected node
1432  const CNodeCombinedStats *stats =
1433  clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
1434  if (stats) {
1435  m_node.ban(stats->nodeStats.addr, bantime);
1437  }
1438  }
1441 }
1442 
1444  if (!clientModel) {
1445  return;
1446  }
1447 
1448  // Get selected ban addresses
1449  QList<QModelIndex> nodes =
1451  for (int i = 0; i < nodes.count(); i++) {
1452  // Get currently selected ban address
1453  QString strNode = nodes.at(i).data().toString();
1454  CSubNet possibleSubnet;
1455 
1456  LookupSubNet(strNode.toStdString(), possibleSubnet);
1457  if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet)) {
1459  }
1460  }
1461 }
1462 
1464  ui->peerWidget->selectionModel()->clearSelection();
1465  cachedNodeids.clear();
1467 }
1468 
1470  if (!clientModel) {
1471  return;
1472  }
1473 
1474  bool visible = clientModel->getBanTableModel()->shouldShow();
1475  ui->banlistWidget->setVisible(visible);
1476  ui->banHeading->setVisible(visible);
1477 }
1478 
1479 void RPCConsole::setTabFocus(enum TabTypes tabType) {
1480  ui->tabWidget->setCurrentIndex(int(tabType));
1481 }
1482 
1483 QString RPCConsole::tabTitle(TabTypes tab_type) const {
1484  return ui->tabWidget->tabText(int(tab_type));
1485 }
1486 
1487 QKeySequence RPCConsole::tabShortcut(TabTypes tab_type) const {
1488  switch (tab_type) {
1489  case TabTypes::INFO:
1490  return QKeySequence(Qt::CTRL + Qt::Key_I);
1491  case TabTypes::CONSOLE:
1492  return QKeySequence(Qt::CTRL + Qt::Key_T);
1493  case TabTypes::GRAPH:
1494  return QKeySequence(Qt::CTRL + Qt::Key_N);
1495  case TabTypes::PEERS:
1496  return QKeySequence(Qt::CTRL + Qt::Key_P);
1497  } // no default case, so the compiler can warn about missing cases
1498 
1499  assert(false);
1500 }
1501 
1502 void RPCConsole::updateAlerts(const QString &warnings) {
1503  this->ui->label_alerts->setVisible(!warnings.isEmpty());
1504  this->ui->label_alerts->setText(warnings);
1505 }
std::string BerkeleyDatabaseVersion()
Definition: bdb.cpp:809
const CChainParams & Params()
Return the currently selected parameters.
bool IsValid() const
Model for Bitcoin network client.
Definition: clientmodel.h:39
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut)
QString blocksDir() const
QString getStatusBarWarnings() const
Return warnings to be displayed in status bar.
interfaces::Node & node() const
Definition: clientmodel.h:54
PeerTableModel * getPeerTableModel()
int getNumConnections(NumConnections flags=CONNECTIONS_ALL) const
Return number of connections, default is in- and outbound (total)
Definition: clientmodel.cpp:68
void numConnectionsChanged(int count)
QString formatClientStartupTime() const
BanTableModel * getBanTableModel()
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
void alertsChanged(const QString &warnings)
QString dataDir() const
QString formatFullVersion() const
QString formatSubVersion() const
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes)
void networkActiveChanged(bool networkActive)
static std::vector< std::string > ToStrings(NetPermissionFlags flags)
const CNodeCombinedStats * getNodeStats(int idx)
int getRowByNodeId(NodeId nodeid)
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
bool getImagesOnButtons() const
Definition: platformstyle.h:20
QImage SingleColorImage(const QString &filename) const
Colorize an image (given filename) with the icon color.
Class for handling RPC timers (used for e.g.
Definition: rpcconsole.cpp:93
std::function< void()> func
Definition: rpcconsole.cpp:105
QtRPCTimerBase(std::function< void()> &_func, int64_t millis)
Definition: rpcconsole.cpp:96
const char * Name() override
Implementation name.
Definition: rpcconsole.cpp:111
RPCTimerBase * NewTimer(std::function< void()> &func, int64_t millis) override
Factory function for timers.
Definition: rpcconsole.cpp:112
Local Bitcoin RPC console.
Definition: rpcconsole.h:36
QMenu * peersTableContextMenu
Definition: rpcconsole.h:166
RPCConsole(interfaces::Node &node, const PlatformStyle *platformStyle, QWidget *parent)
Definition: rpcconsole.cpp:509
void cmdRequest(const QString &command, const WalletModel *wallet_model)
void browseHistory(int offset)
Go forward or back in history.
void fontSmaller()
Definition: rpcconsole.cpp:876
RPCTimerInterface * rpcTimerInterface
Definition: rpcconsole.h:165
QString TimeDurationField(std::chrono::seconds time_now, std::chrono::seconds time_at_event) const
Helper for the output of a time duration field.
Definition: rpcconsole.h:180
void on_lineEdit_returnPressed()
QStringList history
Definition: rpcconsole.h:160
void message(int category, const QString &msg)
Append the message to the message widget.
Definition: rpcconsole.h:109
void setFontSize(int newSize)
Definition: rpcconsole.cpp:880
void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
update traffic statistics
void setTrafficGraphRange(int mins)
static bool RPCParseCommandLine(interfaces::Node *node, std::string &strResult, const std::string &strCommand, bool fExecute, std::string *const pstrFilteredOut=nullptr, const WalletModel *wallet_model=nullptr)
Split shell command line into a list of arguments and optionally execute the command(s).
Definition: rpcconsole.cpp:146
const PlatformStyle *const platformStyle
Definition: rpcconsole.h:164
void updateDetailWidget()
show detailed information on ui about selected node
void showEvent(QShowEvent *event) override
void resizeEvent(QResizeEvent *event) override
static bool RPCExecuteCommandLine(interfaces::Node &node, std::string &strResult, const std::string &strCommand, std::string *const pstrFilteredOut=nullptr, const WalletModel *wallet_model=nullptr)
Definition: rpcconsole.h:50
QString tabTitle(TabTypes tab_type) const
void updateNetworkState()
Update UI with latest network info from model.
Definition: rpcconsole.cpp:993
void disconnectSelectedNode()
Disconnect a selected node on the Peers tab.
@ BANTIME_COLUMN_WIDTH
Definition: rpcconsole.h:153
@ ADDRESS_COLUMN_WIDTH
Definition: rpcconsole.h:149
@ SUBVERSION_COLUMN_WIDTH
Definition: rpcconsole.h:150
@ PING_COLUMN_WIDTH
Definition: rpcconsole.h:151
@ BANSUBNET_COLUMN_WIDTH
Definition: rpcconsole.h:152
QCompleter * autoCompleter
Definition: rpcconsole.h:169
void setMempoolSize(long numberOfTxs, size_t dynUsage)
Set size (number of transactions and memory usage) of the mempool in the UI.
void clear(bool clearHistory=true)
Definition: rpcconsole.cpp:910
void hideEvent(QHideEvent *event) override
QKeySequence tabShortcut(TabTypes tab_type) const
void showPeersTableContextMenu(const QPoint &point)
Show custom context menu on Peers tab.
QList< NodeId > cachedNodeids
Definition: rpcconsole.h:163
interfaces::Node & m_node
Definition: rpcconsole.h:157
void unbanSelectedNode()
Unban a selected node on the Bans tab.
void updateAlerts(const QString &warnings)
void clearSelectedNode()
clear the selected node
void on_sldGraphRange_valueChanged(int value)
change the time range of the network traffic graph
int consoleFontSize
Definition: rpcconsole.h:168
void setNumConnections(int count)
Set number of connections shown in the UI.
void setNumBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType synctype)
Set number of blocks and last block date shown in the UI.
ClientModel * clientModel
Definition: rpcconsole.h:159
void banSelectedNode(int bantime)
Ban a selected node on the Peers tab.
int historyPtr
Definition: rpcconsole.h:161
void scrollToEnd()
Scroll console view to end.
void keyPressEvent(QKeyEvent *) override
Definition: rpcconsole.cpp:969
void on_tabWidget_currentChanged(int index)
Ui::RPCConsole *const ui
Definition: rpcconsole.h:158
void startExecutor()
void setNetworkActive(bool networkActive)
Set network state shown in the UI.
void fontBigger()
Definition: rpcconsole.cpp:872
QString cmdBeforeBrowsing
Definition: rpcconsole.h:162
void addWallet(WalletModel *const walletModel)
virtual bool eventFilter(QObject *obj, QEvent *event) override
Definition: rpcconsole.cpp:598
void on_openDebugLogfileButton_clicked()
open the debug.log from the current datadir
void removeWallet(WalletModel *const walletModel)
void showBanTableContextMenu(const QPoint &point)
Show custom context menu on Bans tab.
void peerLayoutAboutToChange()
Handle selection caching before update.
void setClientModel(ClientModel *model=nullptr, int bestblock_height=0, int64_t bestblock_date=0, double verification_progress=0.0)
Definition: rpcconsole.cpp:653
void setTabFocus(enum TabTypes tabType)
set which tab has the focus (is visible)
WalletModel * m_last_wallet_model
Definition: rpcconsole.h:171
void showOrHideBanTableIfRequired()
Hides ban table if no bans are present.
QMenu * banTableContextMenu
Definition: rpcconsole.h:167
QThread thread
Definition: rpcconsole.h:170
void peerLayoutChanged()
Handle updated peer information.
void reply(int category, const QString &command)
RPCExecutor(interfaces::Node &node)
Definition: rpcconsole.cpp:78
interfaces::Node & m_node
Definition: rpcconsole.cpp:87
void request(const QString &command, const WalletModel *wallet_model)
Definition: rpcconsole.cpp:442
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:91
RPC timer "driver".
Definition: server.h:99
const std::string & get_str() const
bool isArray() const
Definition: univalue.h:95
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:234
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
bool isStr() const
Definition: univalue.h:93
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
bool isObject() const
Definition: univalue.h:96
int get_int() const
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:47
QString getDisplayName() const
static bool isWalletEnabled()
QString getWalletName() const
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:58
virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface)=0
Set RPC timer interface if unset.
virtual bool disconnectById(NodeId id)=0
Disconnect node by id.
virtual std::vector< std::string > listRpcCommands()=0
List rpc commands.
virtual bool ban(const CNetAddr &net_addr, int64_t ban_time_offset)=0
Ban node.
virtual void rpcUnsetTimerInterface(RPCTimerInterface *iface)=0
Unset RPC timer interface.
virtual bool getNetworkActive()=0
Get network active.
virtual bool unban(const CSubNet &ip)=0
Unban node.
virtual bool disconnectByAddress(const CNetAddr &net_addr)=0
Disconnect node by address.
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:222
SyncType
Definition: clientmodel.h:36
const Config & GetConfig()
Definition: config.cpp:34
QString NetworkToQString(Network net)
Convert enum Network to QString.
Definition: guiutil.cpp:787
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:248
QList< QModelIndex > getEntryData(const QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
Definition: guiutil.cpp:272
QString formatBytes(uint64_t bytes)
Definition: guiutil.cpp:891
QString formatDurationStr(std::chrono::seconds dur)
Convert seconds into a QString with days, hours, mins, secs.
Definition: guiutil.cpp:809
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:404
QString formatPingTime(std::chrono::microseconds ping_time)
Format a CNodeStats.m_last_ping_time into a user-readable string or display N/A, if 0.
Definition: guiutil.cpp:849
void openDebugLogfile()
Definition: guiutil.cpp:409
QString formatTimeOffset(int64_t nTimeOffset)
Format a CNodeCombinedStats.nTimeOffset into a user-readable string.
Definition: guiutil.cpp:857
QString formatServicesStr(quint64 mask)
Format CNodeStats.nServices bitmask into a user-readable string.
Definition: guiutil.cpp:833
QFont fixedPitchFont()
Definition: guiutil.cpp:84
Definition: init.h:28
void ThreadRename(std::string &&)
Rename a thread both in terms of an internal (in-memory) name as well as its system thread name.
Definition: threadnames.cpp:48
bool LookupSubNet(const std::string &strSubnet, CSubNet &ret, DNSLookupFn dns_lookup_function)
Parse and resolve a specified subnet string into the appropriate internal representation.
Definition: netbase.cpp:786
NodeContext & m_node
Definition: interfaces.cpp:778
int64_t NodeId
Definition: nodeid.h:10
const struct @7 ICON_MAPPING[]
const int INITIAL_TRAFFIC_GRAPH_MINS
Definition: rpcconsole.cpp:47
const QSize FONT_RANGE(4, 40)
const int CONSOLE_HISTORY
Definition: rpcconsole.cpp:46
static QString categoryClass(int category)
Definition: rpcconsole.cpp:856
const char fontSizeSettingsKey[]
Definition: rpcconsole.cpp:49
const char * url
Definition: rpcconsole.cpp:52
const char * source
Definition: rpcconsole.cpp:53
int atoi(const std::string &str)
constexpr bool IsDigit(char c)
Tests if the given character is a decimal digit.
Definition: strencodings.h:83
CNodeStateStats nodeStateStats
CNodeStats nodeStats
std::chrono::microseconds m_ping_wait
ServiceFlags their_services
std::string addrLocal
Definition: net.h:314
uint64_t nRecvBytes
Definition: net.h:308
std::chrono::microseconds m_last_ping_time
Definition: net.h:311
uint32_t m_mapped_as
Definition: net.h:321
bool fInbound
Definition: net.h:302
uint64_t nSendBytes
Definition: net.h:306
std::chrono::seconds m_last_recv
Definition: net.h:293
std::chrono::seconds m_last_send
Definition: net.h:292
std::chrono::seconds m_last_tx_time
Definition: net.h:294
CAddress addr
Definition: net.h:316
std::chrono::microseconds m_min_ping_time
Definition: net.h:312
int64_t nTimeOffset
Definition: net.h:298
std::chrono::seconds m_connected
Definition: net.h:297
bool m_bip152_highbandwidth_from
Definition: net.h:304
bool m_bip152_highbandwidth_to
Definition: net.h:303
std::string m_addr_name
Definition: net.h:299
int nVersion
Definition: net.h:300
std::chrono::seconds m_last_block_time
Definition: net.h:296
Network m_network
Definition: net.h:320
NodeId nodeid
Definition: net.h:291
NetPermissionFlags m_permissionFlags
Definition: net.h:310
std::string cleanSubVer
Definition: net.h:301
static int count
Definition: tests.c:31
assert(!tx.IsCoinBase())