Bitcoin Core  0.19.99
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)
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 
24 #include <univalue.h>
25 
26 #ifdef ENABLE_WALLET
27 #include <db_cxx.h>
28 #include <wallet/wallet.h>
29 #endif
30 
31 #include <QKeyEvent>
32 #include <QMenu>
33 #include <QMessageBox>
34 #include <QScrollBar>
35 #include <QScreen>
36 #include <QSettings>
37 #include <QTime>
38 #include <QTimer>
39 #include <QStringList>
40 
41 // TODO: add a scrollback limit, as there is currently none
42 // TODO: make it possible to filter out categories (esp debug messages when implemented)
43 // TODO: receive errors and debug messages through ClientModel
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() { return "Qt"; }
119  RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis)
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->messagesWidget->installEventFilter(this);
471 
472  connect(ui->clearButton, &QPushButton::clicked, this, &RPCConsole::clear);
473  connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger);
474  connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller);
475  connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
476 
477  // disable the wallet selector by default
478  ui->WalletSelector->setVisible(false);
479  ui->WalletSelectorLabel->setVisible(false);
480 
481  // set library version labels
482 #ifdef ENABLE_WALLET
483  ui->berkeleyDBVersion->setText(DbEnv::version(nullptr, nullptr, nullptr));
484 #else
485  ui->label_berkeleyDBVersion->hide();
486  ui->berkeleyDBVersion->hide();
487 #endif
488  // Register RPC timer interface
490  // avoid accidentally overwriting an existing, non QTThread
491  // based timer interface
493 
495 
496  ui->detailWidget->hide();
497  ui->peerHeading->setText(tr("Select a peer to view detailed information."));
498 
499  consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt();
500  clear();
501 }
502 
504 {
505  QSettings settings;
506  settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
508  delete rpcTimerInterface;
509  delete ui;
510 }
511 
512 bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
513 {
514  if(event->type() == QEvent::KeyPress) // Special key handling
515  {
516  QKeyEvent *keyevt = static_cast<QKeyEvent*>(event);
517  int key = keyevt->key();
518  Qt::KeyboardModifiers mod = keyevt->modifiers();
519  switch(key)
520  {
521  case Qt::Key_Up: if(obj == ui->lineEdit) { browseHistory(-1); return true; } break;
522  case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break;
523  case Qt::Key_PageUp: /* pass paging keys to messages widget */
524  case Qt::Key_PageDown:
525  if(obj == ui->lineEdit)
526  {
527  QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt));
528  return true;
529  }
530  break;
531  case Qt::Key_Return:
532  case Qt::Key_Enter:
533  // forward these events to lineEdit
534  if(obj == autoCompleter->popup()) {
535  QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
536  autoCompleter->popup()->hide();
537  return true;
538  }
539  break;
540  default:
541  // Typing in messages widget brings focus to line edit, and redirects key there
542  // Exclude most combinations and keys that emit no text, except paste shortcuts
543  if(obj == ui->messagesWidget && (
544  (!mod && !keyevt->text().isEmpty() && key != Qt::Key_Tab) ||
545  ((mod & Qt::ControlModifier) && key == Qt::Key_V) ||
546  ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert)))
547  {
548  ui->lineEdit->setFocus();
549  QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
550  return true;
551  }
552  }
553  }
554  return QWidget::eventFilter(obj, event);
555 }
556 
558 {
559  clientModel = model;
560 
561  bool wallet_enabled{false};
562 #ifdef ENABLE_WALLET
563  wallet_enabled = WalletModel::isWalletEnabled();
564 #endif // ENABLE_WALLET
565  if (model && !wallet_enabled) {
566  // Show warning, for example if this is a prerelease version
567  connect(model, &ClientModel::alertsChanged, this, &RPCConsole::updateAlerts);
569  }
570 
571  ui->trafficGraph->setClientModel(model);
573  // Keep up to date with client
576 
577  interfaces::Node& node = clientModel->node();
578  setNumBlocks(node.getNumBlocks(), QDateTime::fromTime_t(node.getLastBlockTime()), node.getVerificationProgress(), false);
580 
583 
586 
588 
589  // set up peer table
590  ui->peerWidget->setModel(model->getPeerTableModel());
591  ui->peerWidget->verticalHeader()->hide();
592  ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
593  ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
594  ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
595  ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
596  ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
597  ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
598  ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
599  ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
600 
601  // create peer table context menu actions
602  QAction* disconnectAction = new QAction(tr("&Disconnect"), this);
603  QAction* banAction1h = new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
604  QAction* banAction24h = new QAction(tr("Ban for") + " " + tr("1 &day"), this);
605  QAction* banAction7d = new QAction(tr("Ban for") + " " + tr("1 &week"), this);
606  QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this);
607 
608  // create peer table context menu
609  peersTableContextMenu = new QMenu(this);
610  peersTableContextMenu->addAction(disconnectAction);
611  peersTableContextMenu->addAction(banAction1h);
612  peersTableContextMenu->addAction(banAction24h);
613  peersTableContextMenu->addAction(banAction7d);
614  peersTableContextMenu->addAction(banAction365d);
615 
616  connect(banAction1h, &QAction::triggered, [this] { banSelectedNode(60 * 60); });
617  connect(banAction24h, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24); });
618  connect(banAction7d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 7); });
619  connect(banAction365d, &QAction::triggered, [this] { banSelectedNode(60 * 60 * 24 * 365); });
620 
621  // peer table context menu signals
622  connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
623  connect(disconnectAction, &QAction::triggered, this, &RPCConsole::disconnectSelectedNode);
624 
625  // peer table signal handling - update peer details when selecting new node
626  connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::peerSelected);
627  // peer table signal handling - update peer details when new nodes are added to the model
628  connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::peerLayoutChanged);
629  // peer table signal handling - cache selected node ids
630  connect(model->getPeerTableModel(), &PeerTableModel::layoutAboutToBeChanged, this, &RPCConsole::peerLayoutAboutToChange);
631 
632  // set up ban table
633  ui->banlistWidget->setModel(model->getBanTableModel());
634  ui->banlistWidget->verticalHeader()->hide();
635  ui->banlistWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
636  ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
637  ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
638  ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
639  ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
640  ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
641  ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
642 
643  // create ban table context menu action
644  QAction* unbanAction = new QAction(tr("&Unban"), this);
645 
646  // create ban table context menu
647  banTableContextMenu = new QMenu(this);
648  banTableContextMenu->addAction(unbanAction);
649 
650  // ban table context menu signals
651  connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
652  connect(unbanAction, &QAction::triggered, this, &RPCConsole::unbanSelectedNode);
653 
654  // ban table signal handling - clear peer details when clicking a peer in the ban table
655  connect(ui->banlistWidget, &QTableView::clicked, this, &RPCConsole::clearSelectedNode);
656  // ban table signal handling - ensure ban table is shown or hidden (if empty)
657  connect(model->getBanTableModel(), &BanTableModel::layoutChanged, this, &RPCConsole::showOrHideBanTableIfRequired);
659 
660  // Provide initial values
661  ui->clientVersion->setText(model->formatFullVersion());
662  ui->clientUserAgent->setText(model->formatSubVersion());
663  ui->dataDir->setText(model->dataDir());
664  ui->blocksDir->setText(model->blocksDir());
665  ui->startupTime->setText(model->formatClientStartupTime());
666  ui->networkName->setText(QString::fromStdString(Params().NetworkIDString()));
667 
668  //Setup autocomplete and attach it
669  QStringList wordList;
670  std::vector<std::string> commandList = m_node.listRpcCommands();
671  for (size_t i = 0; i < commandList.size(); ++i)
672  {
673  wordList << commandList[i].c_str();
674  wordList << ("help " + commandList[i]).c_str();
675  }
676 
677  wordList << "help-console";
678  wordList.sort();
679  autoCompleter = new QCompleter(wordList, this);
680  autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
681  // ui->lineEdit is initially disabled because running commands is only
682  // possible from now on.
683  ui->lineEdit->setEnabled(true);
684  ui->lineEdit->setCompleter(autoCompleter);
685  autoCompleter->popup()->installEventFilter(this);
686  // Start thread to execute RPC commands.
687  startExecutor();
688  }
689  if (!model) {
690  // Client model is being set to 0, this means shutdown() is about to be called.
691  thread.quit();
692  thread.wait();
693  }
694 }
695 
696 #ifdef ENABLE_WALLET
697 void RPCConsole::addWallet(WalletModel * const walletModel)
698 {
699  // use name for text and wallet model for internal data object (to allow to move to a wallet id later)
700  ui->WalletSelector->addItem(walletModel->getDisplayName(), QVariant::fromValue(walletModel));
701  if (ui->WalletSelector->count() == 2 && !isVisible()) {
702  // First wallet added, set to default so long as the window isn't presently visible (and potentially in use)
703  ui->WalletSelector->setCurrentIndex(1);
704  }
705  if (ui->WalletSelector->count() > 2) {
706  ui->WalletSelector->setVisible(true);
707  ui->WalletSelectorLabel->setVisible(true);
708  }
709 }
710 
711 void RPCConsole::removeWallet(WalletModel * const walletModel)
712 {
713  ui->WalletSelector->removeItem(ui->WalletSelector->findData(QVariant::fromValue(walletModel)));
714  if (ui->WalletSelector->count() == 2) {
715  ui->WalletSelector->setVisible(false);
716  ui->WalletSelectorLabel->setVisible(false);
717  }
718 }
719 #endif
720 
721 static QString categoryClass(int category)
722 {
723  switch(category)
724  {
725  case RPCConsole::CMD_REQUEST: return "cmd-request"; break;
726  case RPCConsole::CMD_REPLY: return "cmd-reply"; break;
727  case RPCConsole::CMD_ERROR: return "cmd-error"; break;
728  default: return "misc";
729  }
730 }
731 
733 {
735 }
736 
738 {
740 }
741 
742 void RPCConsole::setFontSize(int newSize)
743 {
744  QSettings settings;
745 
746  //don't allow an insane font size
747  if (newSize < FONT_RANGE.width() || newSize > FONT_RANGE.height())
748  return;
749 
750  // temp. store the console content
751  QString str = ui->messagesWidget->toHtml();
752 
753  // replace font tags size in current content
754  str.replace(QString("font-size:%1pt").arg(consoleFontSize), QString("font-size:%1pt").arg(newSize));
755 
756  // store the new font size
757  consoleFontSize = newSize;
758  settings.setValue(fontSizeSettingsKey, consoleFontSize);
759 
760  // clear console (reset icon sizes, default stylesheet) and re-add the content
761  float oldPosFactor = 1.0 / ui->messagesWidget->verticalScrollBar()->maximum() * ui->messagesWidget->verticalScrollBar()->value();
762  clear(false);
763  ui->messagesWidget->setHtml(str);
764  ui->messagesWidget->verticalScrollBar()->setValue(oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
765 }
766 
767 void RPCConsole::clear(bool clearHistory)
768 {
769  ui->messagesWidget->clear();
770  if(clearHistory)
771  {
772  history.clear();
773  historyPtr = 0;
774  }
775  ui->lineEdit->clear();
776  ui->lineEdit->setFocus();
777 
778  // Add smoothly scaled icon images.
779  // (when using width/height on an img, Qt uses nearest instead of linear interpolation)
780  for(int i=0; ICON_MAPPING[i].url; ++i)
781  {
782  ui->messagesWidget->document()->addResource(
783  QTextDocument::ImageResource,
784  QUrl(ICON_MAPPING[i].url),
785  platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize*2, consoleFontSize*2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
786  }
787 
788  // Set default style sheet
789  QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont());
790  ui->messagesWidget->document()->setDefaultStyleSheet(
791  QString(
792  "table { }"
793  "td.time { color: #808080; font-size: %2; padding-top: 3px; } "
794  "td.message { font-family: %1; font-size: %2; white-space:pre-wrap; } "
795  "td.cmd-request { color: #006060; } "
796  "td.cmd-error { color: red; } "
797  ".secwarning { color: red; }"
798  "b { color: #006060; } "
799  ).arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize))
800  );
801 
802 #ifdef Q_OS_MAC
803  QString clsKey = "(⌘)-L";
804 #else
805  QString clsKey = "Ctrl-L";
806 #endif
807 
808  message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(PACKAGE_NAME) + "<br>" +
809  tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" +
810  tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" +
811  tr("For more information on using this console type %1.").arg("<b>help-console</b>") +
812  "<br><span class=\"secwarning\"><br>" +
813  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.") +
814  "</span>"),
815  true);
816 }
817 
818 void RPCConsole::keyPressEvent(QKeyEvent *event)
819 {
820  if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape)
821  {
822  close();
823  }
824 }
825 
826 void RPCConsole::message(int category, const QString &message, bool html)
827 {
828  QTime time = QTime::currentTime();
829  QString timeString = time.toString();
830  QString out;
831  out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
832  out += "<td class=\"icon\" width=\"32\"><img src=\"" + categoryClass(category) + "\"></td>";
833  out += "<td class=\"message " + categoryClass(category) + "\" valign=\"middle\">";
834  if(html)
835  out += message;
836  else
837  out += GUIUtil::HtmlEscape(message, false);
838  out += "</td></tr></table>";
839  ui->messagesWidget->append(out);
840 }
841 
843 {
844  QString connections = QString::number(clientModel->getNumConnections()) + " (";
845  connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
846  connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
847 
848  if(!clientModel->node().getNetworkActive()) {
849  connections += " (" + tr("Network activity disabled") + ")";
850  }
851 
852  ui->numberOfConnections->setText(connections);
853 }
854 
856 {
857  if (!clientModel)
858  return;
859 
861 }
862 
863 void RPCConsole::setNetworkActive(bool networkActive)
864 {
866 }
867 
868 void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers)
869 {
870  if (!headers) {
871  ui->numberOfBlocks->setText(QString::number(count));
872  ui->lastBlockTime->setText(blockDate.toString());
873  }
874 }
875 
876 void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage)
877 {
878  ui->mempoolNumberTxs->setText(QString::number(numberOfTxs));
879 
880  if (dynUsage < 1000000)
881  ui->mempoolSize->setText(QString::number(dynUsage/1000.0, 'f', 2) + " KB");
882  else
883  ui->mempoolSize->setText(QString::number(dynUsage/1000000.0, 'f', 2) + " MB");
884 }
885 
887 {
888  QString cmd = ui->lineEdit->text();
889 
890  if(!cmd.isEmpty())
891  {
892  std::string strFilteredCmd;
893  try {
894  std::string dummy;
895  if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) {
896  // Failed to parse command, so we cannot even filter it for the history
897  throw std::runtime_error("Invalid command line");
898  }
899  } catch (const std::exception& e) {
900  QMessageBox::critical(this, "Error", QString("Error: ") + QString::fromStdString(e.what()));
901  return;
902  }
903 
904  ui->lineEdit->clear();
905 
906  cmdBeforeBrowsing = QString();
907 
908 #ifdef ENABLE_WALLET
909  WalletModel* wallet_model = ui->WalletSelector->currentData().value<WalletModel*>();
910 
911  if (m_last_wallet_model != wallet_model) {
912  if (wallet_model) {
913  message(CMD_REQUEST, tr("Executing command using \"%1\" wallet").arg(wallet_model->getWalletName()));
914  } else {
915  message(CMD_REQUEST, tr("Executing command without any wallet"));
916  }
917  m_last_wallet_model = wallet_model;
918  }
919 #endif
920 
921  message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
922  Q_EMIT cmdRequest(cmd, m_last_wallet_model);
923 
924  cmd = QString::fromStdString(strFilteredCmd);
925 
926  // Remove command, if already in history
927  history.removeOne(cmd);
928  // Append command to history
929  history.append(cmd);
930  // Enforce maximum history size
931  while(history.size() > CONSOLE_HISTORY)
932  history.removeFirst();
933  // Set pointer to end of history
934  historyPtr = history.size();
935 
936  // Scroll console view to end
937  scrollToEnd();
938  }
939 }
940 
942 {
943  // store current text when start browsing through the history
944  if (historyPtr == history.size()) {
945  cmdBeforeBrowsing = ui->lineEdit->text();
946  }
947 
948  historyPtr += offset;
949  if(historyPtr < 0)
950  historyPtr = 0;
951  if(historyPtr > history.size())
952  historyPtr = history.size();
953  QString cmd;
954  if(historyPtr < history.size())
955  cmd = history.at(historyPtr);
956  else if (!cmdBeforeBrowsing.isNull()) {
957  cmd = cmdBeforeBrowsing;
958  }
959  ui->lineEdit->setText(cmd);
960 }
961 
963 {
964  RPCExecutor *executor = new RPCExecutor(m_node);
965  executor->moveToThread(&thread);
966 
967  // Replies from executor object must go to this object
968  connect(executor, &RPCExecutor::reply, this, static_cast<void (RPCConsole::*)(int, const QString&)>(&RPCConsole::message));
969 
970  // Requests from this object must go to executor
971  connect(this, &RPCConsole::cmdRequest, executor, &RPCExecutor::request);
972 
973  // Make sure executor object is deleted in its own thread
974  connect(&thread, &QThread::finished, executor, &RPCExecutor::deleteLater);
975 
976  // Default implementation of QThread::run() simply spins up an event loop in the thread,
977  // which is what we want.
978  thread.start();
979 }
980 
982 {
983  if (ui->tabWidget->widget(index) == ui->tab_console) {
984  ui->lineEdit->setFocus();
985  }
986 }
987 
989 {
991 }
992 
994 {
995  QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
996  scrollbar->setValue(scrollbar->maximum());
997 }
998 
1000 {
1001  const int multiplier = 5; // each position on the slider represents 5 min
1002  int mins = value * multiplier;
1003  setTrafficGraphRange(mins);
1004 }
1005 
1007 {
1008  ui->trafficGraph->setGraphRangeMins(mins);
1009  ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
1010 }
1011 
1012 void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
1013 {
1014  ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn));
1015  ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
1016 }
1017 
1018 void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected)
1019 {
1020  Q_UNUSED(deselected);
1021 
1022  if (!clientModel || !clientModel->getPeerTableModel() || selected.indexes().isEmpty())
1023  return;
1024 
1025  const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
1026  if (stats)
1027  updateNodeDetail(stats);
1028 }
1029 
1031 {
1032  QModelIndexList selected = ui->peerWidget->selectionModel()->selectedIndexes();
1033  cachedNodeids.clear();
1034  for(int i = 0; i < selected.size(); i++)
1035  {
1036  const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.at(i).row());
1037  cachedNodeids.append(stats->nodeStats.nodeid);
1038  }
1039 }
1040 
1042 {
1044  return;
1045 
1046  const CNodeCombinedStats *stats = nullptr;
1047  bool fUnselect = false;
1048  bool fReselect = false;
1049 
1050  if (cachedNodeids.empty()) // no node selected yet
1051  return;
1052 
1053  // find the currently selected row
1054  int selectedRow = -1;
1055  QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes();
1056  if (!selectedModelIndex.isEmpty()) {
1057  selectedRow = selectedModelIndex.first().row();
1058  }
1059 
1060  // check if our detail node has a row in the table (it may not necessarily
1061  // be at selectedRow since its position can change after a layout change)
1062  int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.first());
1063 
1064  if (detailNodeRow < 0)
1065  {
1066  // detail node disappeared from table (node disconnected)
1067  fUnselect = true;
1068  }
1069  else
1070  {
1071  if (detailNodeRow != selectedRow)
1072  {
1073  // detail node moved position
1074  fUnselect = true;
1075  fReselect = true;
1076  }
1077 
1078  // get fresh stats on the detail node.
1079  stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
1080  }
1081 
1082  if (fUnselect && selectedRow >= 0) {
1084  }
1085 
1086  if (fReselect)
1087  {
1088  for(int i = 0; i < cachedNodeids.size(); i++)
1089  {
1090  ui->peerWidget->selectRow(clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.at(i)));
1091  }
1092  }
1093 
1094  if (stats)
1095  updateNodeDetail(stats);
1096 }
1097 
1099 {
1100  // update the detail ui with latest node information
1101  QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " ");
1102  peerAddrDetails += tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid));
1103  if (!stats->nodeStats.addrLocal.empty())
1104  peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
1105  ui->peerHeading->setText(peerAddrDetails);
1106  ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
1107  ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastSend) : tr("never"));
1108  ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastRecv) : tr("never"));
1109  ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
1110  ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
1111  ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
1112  ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
1113  ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait));
1114  ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing));
1115  ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
1116  ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion)));
1117  ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
1118  ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
1119  ui->peerHeight->setText(QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight)));
1120  ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No"));
1121 
1122  // This check fails for example if the lock was busy and
1123  // nodeStateStats couldn't be fetched.
1124  if (stats->fNodeStateStatsAvailable) {
1125  // Ban score is init to 0
1126  ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
1127 
1128  // Sync height is init to -1
1129  if (stats->nodeStateStats.nSyncHeight > -1)
1130  ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
1131  else
1132  ui->peerSyncHeight->setText(tr("Unknown"));
1133 
1134  // Common height is init to -1
1135  if (stats->nodeStateStats.nCommonHeight > -1)
1136  ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight));
1137  else
1138  ui->peerCommonHeight->setText(tr("Unknown"));
1139  }
1140 
1141  ui->detailWidget->show();
1142 }
1143 
1144 void RPCConsole::resizeEvent(QResizeEvent *event)
1145 {
1146  QWidget::resizeEvent(event);
1147 }
1148 
1149 void RPCConsole::showEvent(QShowEvent *event)
1150 {
1151  QWidget::showEvent(event);
1152 
1154  return;
1155 
1156  // start PeerTableModel auto refresh
1158 }
1159 
1160 void RPCConsole::hideEvent(QHideEvent *event)
1161 {
1162  QWidget::hideEvent(event);
1163 
1165  return;
1166 
1167  // stop PeerTableModel auto refresh
1169 }
1170 
1171 void RPCConsole::showPeersTableContextMenu(const QPoint& point)
1172 {
1173  QModelIndex index = ui->peerWidget->indexAt(point);
1174  if (index.isValid())
1175  peersTableContextMenu->exec(QCursor::pos());
1176 }
1177 
1178 void RPCConsole::showBanTableContextMenu(const QPoint& point)
1179 {
1180  QModelIndex index = ui->banlistWidget->indexAt(point);
1181  if (index.isValid())
1182  banTableContextMenu->exec(QCursor::pos());
1183 }
1184 
1186 {
1187  // Get selected peer addresses
1188  QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
1189  for(int i = 0; i < nodes.count(); i++)
1190  {
1191  // Get currently selected peer address
1192  NodeId id = nodes.at(i).data().toLongLong();
1193  // Find the node, disconnect it and clear the selected node
1194  if(m_node.disconnect(id))
1196  }
1197 }
1198 
1200 {
1201  if (!clientModel)
1202  return;
1203 
1204  // Get selected peer addresses
1205  QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
1206  for(int i = 0; i < nodes.count(); i++)
1207  {
1208  // Get currently selected peer address
1209  NodeId id = nodes.at(i).data().toLongLong();
1210 
1211  // Get currently selected peer address
1212  int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id);
1213  if (detailNodeRow < 0) return;
1214 
1215  // Find possible nodes, ban it and clear the selected node
1216  const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
1217  if (stats) {
1218  m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
1219  m_node.disconnect(stats->nodeStats.addr);
1220  }
1221  }
1224 }
1225 
1227 {
1228  if (!clientModel)
1229  return;
1230 
1231  // Get selected ban addresses
1232  QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address);
1233  for(int i = 0; i < nodes.count(); i++)
1234  {
1235  // Get currently selected ban address
1236  QString strNode = nodes.at(i).data().toString();
1237  CSubNet possibleSubnet;
1238 
1239  LookupSubNet(strNode.toStdString(), possibleSubnet);
1240  if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet))
1241  {
1243  }
1244  }
1245 }
1246 
1248 {
1249  ui->peerWidget->selectionModel()->clearSelection();
1250  cachedNodeids.clear();
1251  ui->detailWidget->hide();
1252  ui->peerHeading->setText(tr("Select a peer to view detailed information."));
1253 }
1254 
1256 {
1257  if (!clientModel)
1258  return;
1259 
1260  bool visible = clientModel->getBanTableModel()->shouldShow();
1261  ui->banlistWidget->setVisible(visible);
1262  ui->banHeading->setVisible(visible);
1263 }
1264 
1266 {
1267  ui->tabWidget->setCurrentIndex(int(tabType));
1268 }
1269 
1270 QString RPCConsole::tabTitle(TabTypes tab_type) const
1271 {
1272  return ui->tabWidget->tabText(int(tab_type));
1273 }
1274 
1275 QKeySequence RPCConsole::tabShortcut(TabTypes tab_type) const
1276 {
1277  switch (tab_type) {
1278  case TabTypes::INFO: return QKeySequence(Qt::CTRL + Qt::Key_I);
1279  case TabTypes::CONSOLE: return QKeySequence(Qt::CTRL + Qt::Key_T);
1280  case TabTypes::GRAPH: return QKeySequence(Qt::CTRL + Qt::Key_N);
1281  case TabTypes::PEERS: return QKeySequence(Qt::CTRL + Qt::Key_P);
1282  } // no default case, so the compiler can warn about missing cases
1283 
1284  assert(false);
1285 }
1286 
1287 void RPCConsole::updateAlerts(const QString& warnings)
1288 {
1289  this->ui->label_alerts->setVisible(!warnings.isEmpty());
1290  this->ui->label_alerts->setText(warnings);
1291 }
void openDebugLogfile()
Definition: guiutil.cpp:381
QString formatClientStartupTime() const
int getRowByNodeId(NodeId nodeid)
const char * Name()
Implementation name.
Definition: rpcconsole.cpp:118
bool isObject() const
Definition: univalue.h:84
void addWallet(WalletModel *const walletModel)
int nStartingHeight
Definition: net.h:592
QString formatSubVersion() const
Local Bitcoin RPC console.
Definition: rpcconsole.h:36
RPC timer "driver".
Definition: server.h:56
void keyPressEvent(QKeyEvent *)
Definition: rpcconsole.cpp:818
QString cmdBeforeBrowsing
Definition: rpcconsole.h:158
QFont fixedPitchFont()
Definition: guiutil.cpp:81
CNodeStateStats nodeStateStats
virtual bool getNetworkActive()=0
Get network active.
QList< QModelIndex > getEntryData(QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
Definition: guiutil.cpp:246
int64_t nTimeOffset
Definition: net.h:586
void showEvent(QShowEvent *event)
static bool isWalletEnabled()
void on_lineEdit_returnPressed()
Definition: rpcconsole.cpp:886
bool m_legacyWhitelisted
Definition: net.h:598
RPCExecutor(interfaces::Node &node)
Definition: rpcconsole.cpp:82
QString blocksDir() const
virtual double getVerificationProgress()=0
Get verification progress.
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.
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
ServiceFlags nServices
Definition: net.h:581
constexpr bool IsDigit(char c)
Tests if the given character is a decimal digit.
Definition: strencodings.h:69
void setNetworkActive(bool networkActive)
Set network state shown in the UI.
Definition: rpcconsole.cpp:863
void scrollToEnd()
Scroll console view to end.
Definition: rpcconsole.cpp:993
QString formatBytes(uint64_t bytes)
Definition: guiutil.cpp:823
QString formatTimeOffset(int64_t nTimeOffset)
Definition: guiutil.cpp:781
void networkActiveChanged(bool networkActive)
std::string cleanSubVer
Definition: net.h:589
static QString categoryClass(int category)
Definition: rpcconsole.cpp:721
void clearSelectedNode()
clear the selected node
#define PACKAGE_NAME
const struct @8 ICON_MAPPING[]
RPCConsole(interfaces::Node &node, const PlatformStyle *platformStyle, QWidget *parent)
Definition: rpcconsole.cpp:443
const std::string & get_str() const
RPCTimerBase * NewTimer(std::function< void()> &func, int64_t millis)
Factory function for timers.
Definition: rpcconsole.cpp:119
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
int64_t nTimeConnected
Definition: net.h:585
bool isStr() const
Definition: univalue.h:81
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:218
void fontSmaller()
Definition: rpcconsole.cpp:737
PeerTableModel * getPeerTableModel()
void disconnectSelectedNode()
Disconnect a selected node on the Peers tab.
CNodeStats nodeStats
void on_tabWidget_currentChanged(int index)
Definition: rpcconsole.cpp:981
int64_t GetSystemTimeInSeconds()
Returns the system time (not mockable)
Definition: time.cpp:70
void numConnectionsChanged(int count)
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:823
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:222
void setClientModel(ClientModel *model)
Definition: rpcconsole.cpp:557
void resizeEvent(QResizeEvent *event)
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:588
const char * source
Definition: rpcconsole.cpp:52
void browseHistory(int offset)
Go forward or back in history.
Definition: rpcconsole.cpp:941
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
int getNumConnections(unsigned int flags=CONNECTIONS_ALL) const
Return number of connections, default is in- and outbound (total)
Definition: clientmodel.cpp:66
Class for handling RPC timers (used for e.g.
Definition: rpcconsole.cpp:97
QString formatDurationStr(int secs)
Definition: guiutil.cpp:720
const int CONSOLE_HISTORY
Definition: rpcconsole.cpp:45
int atoi(const std::string &str)
void setTabFocus(enum TabTypes tabType)
set which tab has the focus (is visible)
bool fInbound
Definition: net.h:590
BanTableModel * getBanTableModel()
interfaces::Node & node() const
Definition: clientmodel.h:52
uint64_t nRecvBytes
Definition: net.h:595
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
double dPingTime
Definition: net.h:599
std::string addrName
Definition: net.h:587
void updateNetworkState()
Update UI with latest network info from model.
Definition: rpcconsole.cpp:842
int64_t NodeId
Definition: net.h:93
const CNodeCombinedStats * getNodeStats(int idx)
int get_int() const
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, bool header)
QString getWalletName() const
uint64_t nSendBytes
Definition: net.h:593
void on_openDebugLogfileButton_clicked()
open the debug.log from the current datadir
Definition: rpcconsole.cpp:988
QtRPCTimerBase(std::function< void()> &_func, int64_t millis)
Definition: rpcconsole.cpp:101
Model for Bitcoin network client.
Definition: clientmodel.h:44
void unbanSelectedNode()
Unban a selected node on the Bans tab.
void hideEvent(QHideEvent *event)
ClientModel * clientModel
Definition: rpcconsole.h:155
QString formatPingTime(double dPingTime)
Definition: guiutil.cpp:776
virtual bool eventFilter(QObject *obj, QEvent *event)
Definition: rpcconsole.cpp:512
QMenu * banTableContextMenu
Definition: rpcconsole.h:163
void setTrafficGraphRange(int mins)
QKeySequence tabShortcut(TabTypes tab_type) const
void clear(bool clearHistory=true)
Definition: rpcconsole.cpp:767
void showOrHideBanTableIfRequired()
Hides ban table if no bans are present.
void fontBigger()
Definition: rpcconsole.cpp:732
virtual bool ban(const CNetAddr &net_addr, BanReason reason, int64_t ban_time_offset)=0
Ban node.
void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
update traffic statistics
bool IsValid() const
Definition: netaddress.cpp:878
virtual int64_t getLastBlockTime()=0
Get last block time.
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:876
void setFontSize(int newSize)
Definition: rpcconsole.cpp:742
std::function< void()> func
Definition: rpcconsole.cpp:111
void setNumConnections(int count)
Set number of connections shown in the UI.
Definition: rpcconsole.cpp:855
void startExecutor()
Definition: rpcconsole.cpp:962
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:50
virtual std::vector< std::string > listRpcCommands()=0
List rpc commands.
QString formatServicesStr(quint64 mask)
Definition: guiutil.cpp:758
std::string addrLocal
Definition: net.h:604
void on_sldGraphRange_valueChanged(int value)
change the time range of the network traffic graph
Definition: rpcconsole.cpp:999
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)
double dMinPing
Definition: net.h:601
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:47
void removeWallet(WalletModel *const walletModel)
const int INITIAL_TRAFFIC_GRAPH_MINS
Definition: rpcconsole.cpp:46
static int count
Definition: tests.c:45
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
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
virtual int getNumBlocks()=0
Get num blocks.
virtual bool disconnect(const CNetAddr &net_addr)=0
Disconnect node by address.
void cmdRequest(const QString &command, const WalletModel *wallet_model)
QCompleter * autoCompleter
Definition: rpcconsole.h:165
double dPingWait
Definition: net.h:600
virtual int64_t getTotalBytesSent()=0
Get total bytes sent.
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:39
bool isArray() const
Definition: univalue.h:83
bool getImagesOnButtons() const
Definition: platformstyle.h:21
int64_t nLastSend
Definition: net.h:583
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:868
NodeId nodeid
Definition: net.h:580
void showBanTableContextMenu(const QPoint &point)
Show custom context menu on Bans tab.
int64_t nLastRecv
Definition: net.h:584
CAddress addr
Definition: net.h:606
QString formatFullVersion() const