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