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