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