Bitcoin Core 22.99.0
P2P Digital Currency
rpcconsole.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2020 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#if defined(HAVE_CONFIG_H)
7#endif
8
9#include <qt/rpcconsole.h>
11
12#include <chainparams.h>
13#include <interfaces/node.h>
14#include <netbase.h>
15#include <qt/bantablemodel.h>
16#include <qt/clientmodel.h>
18#include <qt/platformstyle.h>
19#include <qt/walletmodel.h>
20#include <rpc/client.h>
21#include <rpc/server.h>
22#include <util/strencodings.h>
23#include <util/string.h>
24#include <util/system.h>
25#include <util/threadnames.h>
26
27#include <univalue.h>
28
29#ifdef ENABLE_WALLET
30#ifdef USE_BDB
31#include <wallet/bdb.h>
32#endif
33#include <wallet/db.h>
34#include <wallet/wallet.h>
35#endif
36
37#include <QAbstractButton>
38#include <QAbstractItemModel>
39#include <QDateTime>
40#include <QFont>
41#include <QKeyEvent>
42#include <QLatin1String>
43#include <QLocale>
44#include <QMenu>
45#include <QMessageBox>
46#include <QScreen>
47#include <QScrollBar>
48#include <QSettings>
49#include <QString>
50#include <QStringList>
51#include <QStyledItemDelegate>
52#include <QTime>
53#include <QTimer>
54#include <QVariant>
55
56const int CONSOLE_HISTORY = 50;
58const QSize FONT_RANGE(4, 40);
59const char fontSizeSettingsKey[] = "consoleFontSize";
60
61const struct {
62 const char *url;
63 const char *source;
64} ICON_MAPPING[] = {
65 {"cmd-request", ":/icons/tx_input"},
66 {"cmd-reply", ":/icons/tx_output"},
67 {"cmd-error", ":/icons/tx_output"},
68 {"misc", ":/icons/tx_inout"},
69 {nullptr, nullptr}
70};
71
72namespace {
73
74// don't add private key handling cmd's to the history
75const QStringList historyFilter = QStringList()
76 << "importprivkey"
77 << "importmulti"
78 << "sethdseed"
79 << "signmessagewithprivkey"
80 << "signrawtransactionwithkey"
81 << "walletpassphrase"
82 << "walletpassphrasechange"
83 << "encryptwallet";
84
85}
86
87/* Object for executing console RPC commands in a separate thread.
88*/
89class RPCExecutor : public QObject
90{
91 Q_OBJECT
92public:
94
95public Q_SLOTS:
96 void request(const QString &command, const WalletModel* wallet_model);
97
98Q_SIGNALS:
99 void reply(int category, const QString &command);
100
101private:
103};
104
108class QtRPCTimerBase: public QObject, public RPCTimerBase
109{
110 Q_OBJECT
111public:
112 QtRPCTimerBase(std::function<void()>& _func, int64_t millis):
113 func(_func)
114 {
115 timer.setSingleShot(true);
116 connect(&timer, &QTimer::timeout, [this]{ func(); });
117 timer.start(millis);
118 }
120private:
121 QTimer timer;
122 std::function<void()> func;
123};
124
126{
127public:
129 const char *Name() override { return "Qt"; }
130 RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
131 {
132 return new QtRPCTimerBase(func, millis);
133 }
134};
135
136class PeerIdViewDelegate : public QStyledItemDelegate
137{
138 Q_OBJECT
139public:
140 explicit PeerIdViewDelegate(QObject* parent = nullptr)
141 : QStyledItemDelegate(parent) {}
142
143 QString displayText(const QVariant& value, const QLocale& locale) const override
144 {
145 // Additional spaces should visually separate right-aligned content
146 // from the next column to the right.
147 return value.toString() + QLatin1String(" ");
148 }
149};
150
151#include <qt/rpcconsole.moc>
152
173bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const WalletModel* wallet_model)
174{
175 std::vector< std::vector<std::string> > stack;
176 stack.push_back(std::vector<std::string>());
177
178 enum CmdParseState
179 {
180 STATE_EATING_SPACES,
181 STATE_EATING_SPACES_IN_ARG,
182 STATE_EATING_SPACES_IN_BRACKETS,
183 STATE_ARGUMENT,
184 STATE_SINGLEQUOTED,
185 STATE_DOUBLEQUOTED,
186 STATE_ESCAPE_OUTER,
187 STATE_ESCAPE_DOUBLEQUOTED,
188 STATE_COMMAND_EXECUTED,
189 STATE_COMMAND_EXECUTED_INNER
190 } state = STATE_EATING_SPACES;
191 std::string curarg;
192 UniValue lastResult;
193 unsigned nDepthInsideSensitive = 0;
194 size_t filter_begin_pos = 0, chpos;
195 std::vector<std::pair<size_t, size_t>> filter_ranges;
196
197 auto add_to_current_stack = [&](const std::string& strArg) {
198 if (stack.back().empty() && (!nDepthInsideSensitive) && historyFilter.contains(QString::fromStdString(strArg), Qt::CaseInsensitive)) {
199 nDepthInsideSensitive = 1;
200 filter_begin_pos = chpos;
201 }
202 // Make sure stack is not empty before adding something
203 if (stack.empty()) {
204 stack.push_back(std::vector<std::string>());
205 }
206 stack.back().push_back(strArg);
207 };
208
209 auto close_out_params = [&]() {
210 if (nDepthInsideSensitive) {
211 if (!--nDepthInsideSensitive) {
212 assert(filter_begin_pos);
213 filter_ranges.push_back(std::make_pair(filter_begin_pos, chpos));
214 filter_begin_pos = 0;
215 }
216 }
217 stack.pop_back();
218 };
219
220 std::string strCommandTerminated = strCommand;
221 if (strCommandTerminated.back() != '\n')
222 strCommandTerminated += "\n";
223 for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos)
224 {
225 char ch = strCommandTerminated[chpos];
226 switch(state)
227 {
228 case STATE_COMMAND_EXECUTED_INNER:
229 case STATE_COMMAND_EXECUTED:
230 {
231 bool breakParsing = true;
232 switch(ch)
233 {
234 case '[': curarg.clear(); state = STATE_COMMAND_EXECUTED_INNER; break;
235 default:
236 if (state == STATE_COMMAND_EXECUTED_INNER)
237 {
238 if (ch != ']')
239 {
240 // append char to the current argument (which is also used for the query command)
241 curarg += ch;
242 break;
243 }
244 if (curarg.size() && fExecute)
245 {
246 // if we have a value query, query arrays with index and objects with a string key
247 UniValue subelement;
248 if (lastResult.isArray())
249 {
250 const auto parsed{ToIntegral<size_t>(curarg)};
251 if (!parsed) {
252 throw std::runtime_error("Invalid result query");
253 }
254 subelement = lastResult[parsed.value()];
255 }
256 else if (lastResult.isObject())
257 subelement = find_value(lastResult, curarg);
258 else
259 throw std::runtime_error("Invalid result query"); //no array or object: abort
260 lastResult = subelement;
261 }
262
263 state = STATE_COMMAND_EXECUTED;
264 break;
265 }
266 // don't break parsing when the char is required for the next argument
267 breakParsing = false;
268
269 // pop the stack and return the result to the current command arguments
270 close_out_params();
271
272 // don't stringify the json in case of a string to avoid doublequotes
273 if (lastResult.isStr())
274 curarg = lastResult.get_str();
275 else
276 curarg = lastResult.write(2);
277
278 // if we have a non empty result, use it as stack argument otherwise as general result
279 if (curarg.size())
280 {
281 if (stack.size())
282 add_to_current_stack(curarg);
283 else
284 strResult = curarg;
285 }
286 curarg.clear();
287 // assume eating space state
288 state = STATE_EATING_SPACES;
289 }
290 if (breakParsing)
291 break;
292 [[fallthrough]];
293 }
294 case STATE_ARGUMENT: // In or after argument
295 case STATE_EATING_SPACES_IN_ARG:
296 case STATE_EATING_SPACES_IN_BRACKETS:
297 case STATE_EATING_SPACES: // Handle runs of whitespace
298 switch(ch)
299 {
300 case '"': state = STATE_DOUBLEQUOTED; break;
301 case '\'': state = STATE_SINGLEQUOTED; break;
302 case '\\': state = STATE_ESCAPE_OUTER; break;
303 case '(': case ')': case '\n':
304 if (state == STATE_EATING_SPACES_IN_ARG)
305 throw std::runtime_error("Invalid Syntax");
306 if (state == STATE_ARGUMENT)
307 {
308 if (ch == '(' && stack.size() && stack.back().size() > 0)
309 {
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 baselevel
317 if (!stack.size())
318 throw std::runtime_error("Invalid Syntax");
319
320 add_to_current_stack(curarg);
321 curarg.clear();
322 state = STATE_EATING_SPACES_IN_BRACKETS;
323 }
324 if ((ch == ')' || ch == '\n') && stack.size() > 0)
325 {
326 if (fExecute) {
327 // Convert argument list to JSON objects in method-dependent way,
328 // and pass it along with the method name to the dispatcher.
329 UniValue params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()));
330 std::string method = stack.back()[0];
331 std::string uri;
332#ifdef ENABLE_WALLET
333 if (wallet_model) {
334 QByteArray encodedName = QUrl::toPercentEncoding(wallet_model->getWalletName());
335 uri = "/wallet/"+std::string(encodedName.constData(), encodedName.length());
336 }
337#endif
338 assert(node);
339 lastResult = node->executeRpc(method, params, uri);
340 }
341
342 state = STATE_COMMAND_EXECUTED;
343 curarg.clear();
344 }
345 break;
346 case ' ': case ',': case '\t':
347 if(state == STATE_EATING_SPACES_IN_ARG && curarg.empty() && ch == ',')
348 throw std::runtime_error("Invalid Syntax");
349
350 else if(state == STATE_ARGUMENT) // Space ends argument
351 {
352 add_to_current_stack(curarg);
353 curarg.clear();
354 }
355 if ((state == STATE_EATING_SPACES_IN_BRACKETS || state == STATE_ARGUMENT) && ch == ',')
356 {
357 state = STATE_EATING_SPACES_IN_ARG;
358 break;
359 }
360 state = STATE_EATING_SPACES;
361 break;
362 default: curarg += ch; state = STATE_ARGUMENT;
363 }
364 break;
365 case STATE_SINGLEQUOTED: // Single-quoted string
366 switch(ch)
367 {
368 case '\'': state = STATE_ARGUMENT; break;
369 default: curarg += ch;
370 }
371 break;
372 case STATE_DOUBLEQUOTED: // Double-quoted string
373 switch(ch)
374 {
375 case '"': state = STATE_ARGUMENT; break;
376 case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break;
377 default: curarg += ch;
378 }
379 break;
380 case STATE_ESCAPE_OUTER: // '\' outside quotes
381 curarg += ch; state = STATE_ARGUMENT;
382 break;
383 case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
384 if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself
385 curarg += ch; state = STATE_DOUBLEQUOTED;
386 break;
387 }
388 }
389 if (pstrFilteredOut) {
390 if (STATE_COMMAND_EXECUTED == state) {
391 assert(!stack.empty());
392 close_out_params();
393 }
394 *pstrFilteredOut = strCommand;
395 for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) {
396 pstrFilteredOut->replace(i->first, i->second - i->first, "(…)");
397 }
398 }
399 switch(state) // final state
400 {
401 case STATE_COMMAND_EXECUTED:
402 if (lastResult.isStr())
403 strResult = lastResult.get_str();
404 else
405 strResult = lastResult.write(2);
406 [[fallthrough]];
407 case STATE_ARGUMENT:
408 case STATE_EATING_SPACES:
409 return true;
410 default: // ERROR to end in one of the other states
411 return false;
412 }
413}
414
415void RPCExecutor::request(const QString &command, const WalletModel* wallet_model)
416{
417 try
418 {
419 std::string result;
420 std::string executableCommand = command.toStdString() + "\n";
421
422 // Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply.
423 if(executableCommand == "help-console\n") {
424 Q_EMIT reply(RPCConsole::CMD_REPLY, QString(("\n"
425 "This console accepts RPC commands using the standard syntax.\n"
426 " example: getblockhash 0\n\n"
427
428 "This console can also accept RPC commands using the parenthesized syntax.\n"
429 " example: getblockhash(0)\n\n"
430
431 "Commands may be nested when specified with the parenthesized syntax.\n"
432 " example: getblock(getblockhash(0) 1)\n\n"
433
434 "A space or a comma can be used to delimit arguments for either syntax.\n"
435 " example: getblockhash 0\n"
436 " getblockhash,0\n\n"
437
438 "Named results can be queried with a non-quoted key string in brackets using the parenthesized syntax.\n"
439 " example: getblock(getblockhash(0) 1)[tx]\n\n"
440
441 "Results without keys can be queried with an integer in brackets using the parenthesized syntax.\n"
442 " example: getblock(getblockhash(0),1)[tx][0]\n\n")));
443 return;
444 }
445 if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, wallet_model)) {
446 Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
447 return;
448 }
449
450 Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
451 }
452 catch (UniValue& objError)
453 {
454 try // Nice formatting for standard-format error
455 {
456 int code = find_value(objError, "code").get_int();
457 std::string message = find_value(objError, "message").get_str();
458 Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")");
459 }
460 catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message
461 { // Show raw JSON object
462 Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(objError.write()));
463 }
464 }
465 catch (const std::exception& e)
466 {
467 Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what()));
468 }
469}
470
471RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformStyle, QWidget *parent) :
472 QWidget(parent),
473 m_node(node),
474 ui(new Ui::RPCConsole),
475 platformStyle(_platformStyle)
476{
477 ui->setupUi(this);
478 QSettings settings;
479#ifdef ENABLE_WALLET
481 // RPCConsole widget is a window.
482 if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
483 // Restore failed (perhaps missing setting), center the window
484 move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
485 }
486 ui->splitter->restoreState(settings.value("RPCConsoleWindowPeersTabSplitterSizes").toByteArray());
487 } else
488#endif // ENABLE_WALLET
489 {
490 // RPCConsole is a child widget.
491 ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray());
492 }
493
494 m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray();
495 m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray();
496
497 constexpr QChar nonbreaking_hyphen(8209);
498 const std::vector<QString> CONNECTION_TYPE_DOC{
499 //: Explanatory text for an inbound peer connection.
500 tr("Inbound: initiated by peer"),
501 /*: Explanatory text for an outbound peer connection that
502 relays all network information. This is the default behavior for
503 outbound connections. */
504 tr("Outbound Full Relay: default"),
505 /*: Explanatory text for an outbound peer connection that relays
506 network information about blocks and not transactions or addresses. */
507 tr("Outbound Block Relay: does not relay transactions or addresses"),
508 /*: Explanatory text for an outbound peer connection that was
509 established manually through one of several methods. The numbered
510 arguments are stand-ins for the methods available to establish
511 manual connections. */
512 tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options")
513 .arg("addnode")
514 .arg(QString(nonbreaking_hyphen) + "addnode")
515 .arg(QString(nonbreaking_hyphen) + "connect"),
516 /*: Explanatory text for a short-lived outbound peer connection that
517 is used to test the aliveness of known addresses. */
518 tr("Outbound Feeler: short-lived, for testing addresses"),
519 /*: Explanatory text for a short-lived outbound peer connection that is used
520 to request addresses from a peer. */
521 tr("Outbound Address Fetch: short-lived, for soliciting addresses")};
522 const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
523 ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list));
524 const QString hb_list{"<ul><li>\""
525 + ts.to + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\""
526 + ts.from + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\""
527 + ts.no + "\" – " + tr("no high bandwidth relay selected") + "</li></ul>"};
528 ui->peerHighBandwidthLabel->setToolTip(ui->peerHighBandwidthLabel->toolTip().arg(hb_list));
529 ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
530 ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir"));
531 ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME));
532
534 ui->openDebugLogfileButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
535 }
536 ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
537
538 ui->fontBiggerButton->setIcon(platformStyle->SingleColorIcon(":/icons/fontbigger"));
539 //: Main shortcut to increase the RPC console font size.
540 ui->fontBiggerButton->setShortcut(tr("Ctrl++"));
541 //: Secondary shortcut to increase the RPC console font size.
543
544 ui->fontSmallerButton->setIcon(platformStyle->SingleColorIcon(":/icons/fontsmaller"));
545 //: Main shortcut to decrease the RPC console font size.
546 ui->fontSmallerButton->setShortcut(tr("Ctrl+-"));
547 //: Secondary shortcut to decrease the RPC console font size.
549
550 ui->promptIcon->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/prompticon")));
551
552 // Install event filter for up and down arrow
553 ui->lineEdit->installEventFilter(this);
554 ui->lineEdit->setMaxLength(16 * 1024 * 1024);
555 ui->messagesWidget->installEventFilter(this);
556
557 connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); });
558 connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger);
559 connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller);
560 connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
561
562 // disable the wallet selector by default
563 ui->WalletSelector->setVisible(false);
564 ui->WalletSelectorLabel->setVisible(false);
565
566 // Register RPC timer interface
568 // avoid accidentally overwriting an existing, non QTThread
569 // based timer interface
571
574
575 consoleFontSize = settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt();
576 clear();
577
579}
580
582{
583 QSettings settings;
584#ifdef ENABLE_WALLET
586 // RPCConsole widget is a window.
587 settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
588 settings.setValue("RPCConsoleWindowPeersTabSplitterSizes", ui->splitter->saveState());
589 } else
590#endif // ENABLE_WALLET
591 {
592 // RPCConsole is a child widget.
593 settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState());
594 }
595
596 settings.setValue("PeersTabPeerHeaderState", m_peer_widget_header_state);
597 settings.setValue("PeersTabBanlistHeaderState", m_banlist_widget_header_state);
598
600 delete rpcTimerInterface;
601 delete ui;
602}
603
604bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
605{
606 if(event->type() == QEvent::KeyPress) // Special key handling
607 {
608 QKeyEvent *keyevt = static_cast<QKeyEvent*>(event);
609 int key = keyevt->key();
610 Qt::KeyboardModifiers mod = keyevt->modifiers();
611 switch(key)
612 {
613 case Qt::Key_Up: if(obj == ui->lineEdit) { browseHistory(-1); return true; } break;
614 case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break;
615 case Qt::Key_PageUp: /* pass paging keys to messages widget */
616 case Qt::Key_PageDown:
617 if(obj == ui->lineEdit)
618 {
619 QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt));
620 return true;
621 }
622 break;
623 case Qt::Key_Return:
624 case Qt::Key_Enter:
625 // forward these events to lineEdit
626 if(obj == autoCompleter->popup()) {
627 QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
628 autoCompleter->popup()->hide();
629 return true;
630 }
631 break;
632 default:
633 // Typing in messages widget brings focus to line edit, and redirects key there
634 // Exclude most combinations and keys that emit no text, except paste shortcuts
635 if(obj == ui->messagesWidget && (
636 (!mod && !keyevt->text().isEmpty() && key != Qt::Key_Tab) ||
637 ((mod & Qt::ControlModifier) && key == Qt::Key_V) ||
638 ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert)))
639 {
640 ui->lineEdit->setFocus();
641 QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
642 return true;
643 }
644 }
645 }
646 return QWidget::eventFilter(obj, event);
647}
648
649void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_t bestblock_date, double verification_progress)
650{
651 clientModel = model;
652
653 bool wallet_enabled{false};
654#ifdef ENABLE_WALLET
655 wallet_enabled = WalletModel::isWalletEnabled();
656#endif // ENABLE_WALLET
657 if (model && !wallet_enabled) {
658 // Show warning, for example if this is a prerelease version
661 }
662
665 // Keep up to date with client
668
669 setNumBlocks(bestblock_height, QDateTime::fromSecsSinceEpoch(bestblock_date), verification_progress, false);
671
674
676 updateTrafficStats(node.getTotalBytesRecv(), node.getTotalBytesSent());
678
680
681 // set up peer table
682 ui->peerWidget->setModel(model->peerTableSortProxy());
683 ui->peerWidget->verticalHeader()->hide();
684 ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
685 ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
686 ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
687
688 if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
692 }
693 ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
694 ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
695
696 // create peer table context menu
697 peersTableContextMenu = new QMenu(this);
698 //: Context menu action to copy the address of a peer.
699 peersTableContextMenu->addAction(tr("&Copy address"), [this] {
701 });
702 peersTableContextMenu->addSeparator();
703 peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode);
704 peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); });
705 peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); });
706 peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
707 peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
708 connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
709
710 // peer table signal handling - update peer details when selecting new node
711 connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget);
712 connect(model->getPeerTableModel(), &QAbstractItemModel::dataChanged, [this] { updateDetailWidget(); });
713
714 // set up ban table
715 ui->banlistWidget->setModel(model->getBanTableModel());
716 ui->banlistWidget->verticalHeader()->hide();
717 ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
718 ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
719 ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
720
721 if (!ui->banlistWidget->horizontalHeader()->restoreState(m_banlist_widget_header_state)) {
724 }
725 ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
726
727 // create ban table context menu
728 banTableContextMenu = new QMenu(this);
729 /*: Context menu action to copy the IP/Netmask of a banned peer.
730 IP/Netmask is the combination of a peer's IP address and its Netmask.
731 For IP address, see: https://en.wikipedia.org/wiki/IP_address. */
732 banTableContextMenu->addAction(tr("&Copy IP/Netmask"), [this] {
734 });
735 banTableContextMenu->addSeparator();
736 banTableContextMenu->addAction(tr("&Unban"), this, &RPCConsole::unbanSelectedNode);
737 connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
738
739 // ban table signal handling - clear peer details when clicking a peer in the ban table
740 connect(ui->banlistWidget, &QTableView::clicked, this, &RPCConsole::clearSelectedNode);
741 // ban table signal handling - ensure ban table is shown or hidden (if empty)
742 connect(model->getBanTableModel(), &BanTableModel::layoutChanged, this, &RPCConsole::showOrHideBanTableIfRequired);
744
745 // Provide initial values
746 ui->clientVersion->setText(model->formatFullVersion());
747 ui->clientUserAgent->setText(model->formatSubVersion());
748 ui->dataDir->setText(model->dataDir());
749 ui->blocksDir->setText(model->blocksDir());
750 ui->startupTime->setText(model->formatClientStartupTime());
751 ui->networkName->setText(QString::fromStdString(Params().NetworkIDString()));
752
753 //Setup autocomplete and attach it
754 QStringList wordList;
755 std::vector<std::string> commandList = m_node.listRpcCommands();
756 for (size_t i = 0; i < commandList.size(); ++i)
757 {
758 wordList << commandList[i].c_str();
759 wordList << ("help " + commandList[i]).c_str();
760 }
761
762 wordList << "help-console";
763 wordList.sort();
764 autoCompleter = new QCompleter(wordList, this);
765 autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
766 // ui->lineEdit is initially disabled because running commands is only
767 // possible from now on.
768 ui->lineEdit->setEnabled(true);
769 ui->lineEdit->setCompleter(autoCompleter);
770 autoCompleter->popup()->installEventFilter(this);
771 // Start thread to execute RPC commands.
773 }
774 if (!model) {
775 // Client model is being set to 0, this means shutdown() is about to be called.
776 thread.quit();
777 thread.wait();
778 }
779}
780
781#ifdef ENABLE_WALLET
782void RPCConsole::addWallet(WalletModel * const walletModel)
783{
784 // use name for text and wallet model for internal data object (to allow to move to a wallet id later)
785 ui->WalletSelector->addItem(walletModel->getDisplayName(), QVariant::fromValue(walletModel));
786 if (ui->WalletSelector->count() == 2 && !isVisible()) {
787 // First wallet added, set to default so long as the window isn't presently visible (and potentially in use)
788 ui->WalletSelector->setCurrentIndex(1);
789 }
790 if (ui->WalletSelector->count() > 2) {
791 ui->WalletSelector->setVisible(true);
792 ui->WalletSelectorLabel->setVisible(true);
793 }
794}
795
796void RPCConsole::removeWallet(WalletModel * const walletModel)
797{
798 ui->WalletSelector->removeItem(ui->WalletSelector->findData(QVariant::fromValue(walletModel)));
799 if (ui->WalletSelector->count() == 2) {
800 ui->WalletSelector->setVisible(false);
801 ui->WalletSelectorLabel->setVisible(false);
802 }
803}
804#endif
805
806static QString categoryClass(int category)
807{
808 switch(category)
809 {
810 case RPCConsole::CMD_REQUEST: return "cmd-request"; break;
811 case RPCConsole::CMD_REPLY: return "cmd-reply"; break;
812 case RPCConsole::CMD_ERROR: return "cmd-error"; break;
813 default: return "misc";
814 }
815}
816
818{
820}
821
823{
825}
826
827void RPCConsole::setFontSize(int newSize)
828{
829 QSettings settings;
830
831 //don't allow an insane font size
832 if (newSize < FONT_RANGE.width() || newSize > FONT_RANGE.height())
833 return;
834
835 // temp. store the console content
836 QString str = ui->messagesWidget->toHtml();
837
838 // replace font tags size in current content
839 str.replace(QString("font-size:%1pt").arg(consoleFontSize), QString("font-size:%1pt").arg(newSize));
840
841 // store the new font size
842 consoleFontSize = newSize;
843 settings.setValue(fontSizeSettingsKey, consoleFontSize);
844
845 // clear console (reset icon sizes, default stylesheet) and re-add the content
846 float oldPosFactor = 1.0 / ui->messagesWidget->verticalScrollBar()->maximum() * ui->messagesWidget->verticalScrollBar()->value();
847 clear(/* keep_prompt */ true);
848 ui->messagesWidget->setHtml(str);
849 ui->messagesWidget->verticalScrollBar()->setValue(oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
850}
851
852void RPCConsole::clear(bool keep_prompt)
853{
854 ui->messagesWidget->clear();
855 if (!keep_prompt) ui->lineEdit->clear();
856 ui->lineEdit->setFocus();
857
858 // Add smoothly scaled icon images.
859 // (when using width/height on an img, Qt uses nearest instead of linear interpolation)
860 for(int i=0; ICON_MAPPING[i].url; ++i)
861 {
862 ui->messagesWidget->document()->addResource(
863 QTextDocument::ImageResource,
864 QUrl(ICON_MAPPING[i].url),
865 platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize*2, consoleFontSize*2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
866 }
867
868 // Set default style sheet
869 QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont());
870 ui->messagesWidget->document()->setDefaultStyleSheet(
871 QString(
872 "table { }"
873 "td.time { color: #808080; font-size: %2; padding-top: 3px; } "
874 "td.message { font-family: %1; font-size: %2; white-space:pre-wrap; } "
875 "td.cmd-request { color: #006060; } "
876 "td.cmd-error { color: red; } "
877 ".secwarning { color: red; }"
878 "b { color: #006060; } "
879 ).arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize))
880 );
881
882 static const QString welcome_message =
883 /*: RPC console welcome message.
884 Placeholders %7 and %8 are style tags for the warning content, and
885 they are not space separated from the rest of the text intentionally. */
886 tr("Welcome to the %1 RPC console.\n"
887 "Use up and down arrows to navigate history, and %2 to clear screen.\n"
888 "Use %3 and %4 to increase or decrease the font size.\n"
889 "Type %5 for an overview of available commands.\n"
890 "For more information on using this console, type %6.\n"
891 "\n"
892 "%7WARNING: Scammers have been active, telling users to type"
893 " commands here, stealing their wallet contents. Do not use this console"
894 " without fully understanding the ramifications of a command.%8")
895 .arg(PACKAGE_NAME,
896 "<b>" + ui->clearButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
897 "<b>" + ui->fontBiggerButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
898 "<b>" + ui->fontSmallerButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
899 "<b>help</b>",
900 "<b>help-console</b>",
901 "<span class=\"secwarning\">",
902 "<span>");
903
904 message(CMD_REPLY, welcome_message, true);
905}
906
907void RPCConsole::keyPressEvent(QKeyEvent *event)
908{
909 if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape)
910 {
911 close();
912 }
913}
914
916{
917 if (e->type() == QEvent::PaletteChange) {
918 ui->clearButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
919 ui->fontBiggerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontbigger")));
920 ui->fontSmallerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontsmaller")));
921 ui->promptIcon->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/prompticon")));
922
923 for (int i = 0; ICON_MAPPING[i].url; ++i) {
924 ui->messagesWidget->document()->addResource(
925 QTextDocument::ImageResource,
926 QUrl(ICON_MAPPING[i].url),
927 platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize * 2, consoleFontSize * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
928 }
929 }
930
931 QWidget::changeEvent(e);
932}
933
934void RPCConsole::message(int category, const QString &message, bool html)
935{
936 QTime time = QTime::currentTime();
937 QString timeString = time.toString();
938 QString out;
939 out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
940 out += "<td class=\"icon\" width=\"32\"><img src=\"" + categoryClass(category) + "\"></td>";
941 out += "<td class=\"message " + categoryClass(category) + "\" valign=\"middle\">";
942 if(html)
943 out += message;
944 else
945 out += GUIUtil::HtmlEscape(message, false);
946 out += "</td></tr></table>";
947 ui->messagesWidget->append(out);
948}
949
951{
952 QString connections = QString::number(clientModel->getNumConnections()) + " (";
953 connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
954 connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
955
957 connections += " (" + tr("Network activity disabled") + ")";
958 }
959
960 ui->numberOfConnections->setText(connections);
961}
962
964{
965 if (!clientModel)
966 return;
967
969}
970
971void RPCConsole::setNetworkActive(bool networkActive)
972{
974}
975
976void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers)
977{
978 if (!headers) {
979 ui->numberOfBlocks->setText(QString::number(count));
980 ui->lastBlockTime->setText(blockDate.toString());
981 }
982}
983
984void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage)
985{
986 ui->mempoolNumberTxs->setText(QString::number(numberOfTxs));
987
988 if (dynUsage < 1000000)
989 ui->mempoolSize->setText(QString::number(dynUsage/1000.0, 'f', 2) + " KB");
990 else
991 ui->mempoolSize->setText(QString::number(dynUsage/1000000.0, 'f', 2) + " MB");
992}
993
995{
996 QString cmd = ui->lineEdit->text().trimmed();
997
998 if (cmd.isEmpty()) {
999 return;
1000 }
1001
1002 std::string strFilteredCmd;
1003 try {
1004 std::string dummy;
1005 if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) {
1006 // Failed to parse command, so we cannot even filter it for the history
1007 throw std::runtime_error("Invalid command line");
1008 }
1009 } catch (const std::exception& e) {
1010 QMessageBox::critical(this, "Error", QString("Error: ") + QString::fromStdString(e.what()));
1011 return;
1012 }
1013
1014 // A special case allows to request shutdown even a long-running command is executed.
1015 if (cmd == QLatin1String("stop")) {
1016 std::string dummy;
1017 RPCExecuteCommandLine(m_node, dummy, cmd.toStdString());
1018 return;
1019 }
1020
1021 if (m_is_executing) {
1022 return;
1023 }
1024
1025 ui->lineEdit->clear();
1026
1027#ifdef ENABLE_WALLET
1028 WalletModel* wallet_model = ui->WalletSelector->currentData().value<WalletModel*>();
1029
1030 if (m_last_wallet_model != wallet_model) {
1031 if (wallet_model) {
1032 message(CMD_REQUEST, tr("Executing command using \"%1\" wallet").arg(wallet_model->getWalletName()));
1033 } else {
1034 message(CMD_REQUEST, tr("Executing command without any wallet"));
1035 }
1036 m_last_wallet_model = wallet_model;
1037 }
1038#endif // ENABLE_WALLET
1039
1040 message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
1041 //: A console message indicating an entered command is currently being executed.
1042 message(CMD_REPLY, tr("Executing…"));
1043 m_is_executing = true;
1044 Q_EMIT cmdRequest(cmd, m_last_wallet_model);
1045
1046 cmd = QString::fromStdString(strFilteredCmd);
1047
1048 // Remove command, if already in history
1049 history.removeOne(cmd);
1050 // Append command to history
1051 history.append(cmd);
1052 // Enforce maximum history size
1053 while (history.size() > CONSOLE_HISTORY) {
1054 history.removeFirst();
1055 }
1056 // Set pointer to end of history
1057 historyPtr = history.size();
1058
1059 // Scroll console view to end
1060 scrollToEnd();
1061}
1062
1064{
1065 // store current text when start browsing through the history
1066 if (historyPtr == history.size()) {
1067 cmdBeforeBrowsing = ui->lineEdit->text();
1068 }
1069
1070 historyPtr += offset;
1071 if(historyPtr < 0)
1072 historyPtr = 0;
1073 if(historyPtr > history.size())
1074 historyPtr = history.size();
1075 QString cmd;
1076 if(historyPtr < history.size())
1077 cmd = history.at(historyPtr);
1078 else if (!cmdBeforeBrowsing.isNull()) {
1079 cmd = cmdBeforeBrowsing;
1080 }
1081 ui->lineEdit->setText(cmd);
1082}
1083
1085{
1086 RPCExecutor *executor = new RPCExecutor(m_node);
1087 executor->moveToThread(&thread);
1088
1089 // Replies from executor object must go to this object
1090 connect(executor, &RPCExecutor::reply, this, [this](int category, const QString& command) {
1091 // Remove "Executing…" message.
1092 ui->messagesWidget->undo();
1093 message(category, command);
1094 scrollToEnd();
1095 m_is_executing = false;
1096 });
1097
1098 // Requests from this object must go to executor
1099 connect(this, &RPCConsole::cmdRequest, executor, &RPCExecutor::request);
1100
1101 // Make sure executor object is deleted in its own thread
1102 connect(&thread, &QThread::finished, executor, &RPCExecutor::deleteLater);
1103
1104 // Default implementation of QThread::run() simply spins up an event loop in the thread,
1105 // which is what we want.
1106 thread.start();
1107 QTimer::singleShot(0, executor, []() {
1108 util::ThreadRename("qt-rpcconsole");
1109 });
1110}
1111
1113{
1114 if (ui->tabWidget->widget(index) == ui->tab_console) {
1115 ui->lineEdit->setFocus();
1116 }
1117}
1118
1120{
1122}
1123
1125{
1126 QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
1127 scrollbar->setValue(scrollbar->maximum());
1128}
1129
1131{
1132 const int multiplier = 5; // each position on the slider represents 5 min
1133 int mins = value * multiplier;
1135}
1136
1138{
1140 ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
1141}
1142
1143void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
1144{
1145 ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn));
1146 ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
1147}
1148
1150{
1151 const QList<QModelIndex> selected_peers = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
1152 if (!clientModel || !clientModel->getPeerTableModel() || selected_peers.size() != 1) {
1153 ui->peersTabRightPanel->hide();
1154 ui->peerHeading->setText(tr("Select a peer to view detailed information."));
1155 return;
1156 }
1157 const auto stats = selected_peers.first().data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>();
1158 // update the detail ui with latest node information
1159 QString peerAddrDetails(QString::fromStdString(stats->nodeStats.m_addr_name) + " ");
1160 peerAddrDetails += tr("(peer: %1)").arg(QString::number(stats->nodeStats.nodeid));
1161 if (!stats->nodeStats.addrLocal.empty())
1162 peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
1163 ui->peerHeading->setText(peerAddrDetails);
1164 ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
1165 ui->peerRelayTxes->setText(stats->nodeStats.fRelayTxes ? ts.yes : ts.no);
1166 QString bip152_hb_settings;
1167 if (stats->nodeStats.m_bip152_highbandwidth_to) bip152_hb_settings = ts.to;
1168 if (stats->nodeStats.m_bip152_highbandwidth_from) bip152_hb_settings += (bip152_hb_settings.isEmpty() ? ts.from : QLatin1Char('/') + ts.from);
1169 if (bip152_hb_settings.isEmpty()) bip152_hb_settings = ts.no;
1170 ui->peerHighBandwidth->setText(bip152_hb_settings);
1171 const int64_t time_now{GetTimeSeconds()};
1172 ui->peerConnTime->setText(GUIUtil::formatDurationStr(time_now - stats->nodeStats.nTimeConnected));
1173 ui->peerLastBlock->setText(TimeDurationField(time_now, stats->nodeStats.nLastBlockTime));
1174 ui->peerLastTx->setText(TimeDurationField(time_now, stats->nodeStats.nLastTXTime));
1175 ui->peerLastSend->setText(TimeDurationField(time_now, stats->nodeStats.nLastSend));
1176 ui->peerLastRecv->setText(TimeDurationField(time_now, stats->nodeStats.nLastRecv));
1177 ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
1178 ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
1179 ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time));
1180 ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_time));
1181 ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
1182 ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
1183 ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
1184 ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true));
1185 ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network));
1186 if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) {
1187 ui->peerPermissions->setText(ts.na);
1188 } else {
1189 QStringList permissions;
1190 for (const auto& permission : NetPermissions::ToStrings(stats->nodeStats.m_permissionFlags)) {
1191 permissions.append(QString::fromStdString(permission));
1192 }
1193 ui->peerPermissions->setText(permissions.join(" & "));
1194 }
1195 ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : ts.na);
1196
1197 // This check fails for example if the lock was busy and
1198 // nodeStateStats couldn't be fetched.
1199 if (stats->fNodeStateStatsAvailable) {
1200 // Sync height is init to -1
1201 if (stats->nodeStateStats.nSyncHeight > -1) {
1202 ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
1203 } else {
1204 ui->peerSyncHeight->setText(ts.unknown);
1205 }
1206 // Common height is init to -1
1207 if (stats->nodeStateStats.nCommonHeight > -1) {
1208 ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight));
1209 } else {
1210 ui->peerCommonHeight->setText(ts.unknown);
1211 }
1212 ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height));
1213 ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait));
1214 }
1215
1216 ui->peersTabRightPanel->show();
1217}
1218
1219void RPCConsole::resizeEvent(QResizeEvent *event)
1220{
1221 QWidget::resizeEvent(event);
1222}
1223
1224void RPCConsole::showEvent(QShowEvent *event)
1225{
1226 QWidget::showEvent(event);
1227
1229 return;
1230
1231 // start PeerTableModel auto refresh
1233}
1234
1235void RPCConsole::hideEvent(QHideEvent *event)
1236{
1237 // It is too late to call QHeaderView::saveState() in ~RPCConsole(), as all of
1238 // the columns of QTableView child widgets will have zero width at that moment.
1239 m_peer_widget_header_state = ui->peerWidget->horizontalHeader()->saveState();
1240 m_banlist_widget_header_state = ui->banlistWidget->horizontalHeader()->saveState();
1241
1242 QWidget::hideEvent(event);
1243
1245 return;
1246
1247 // stop PeerTableModel auto refresh
1249}
1250
1252{
1253 QModelIndex index = ui->peerWidget->indexAt(point);
1254 if (index.isValid())
1255 peersTableContextMenu->exec(QCursor::pos());
1256}
1257
1259{
1260 QModelIndex index = ui->banlistWidget->indexAt(point);
1261 if (index.isValid())
1262 banTableContextMenu->exec(QCursor::pos());
1263}
1264
1266{
1267 // Get selected peer addresses
1268 QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
1269 for(int i = 0; i < nodes.count(); i++)
1270 {
1271 // Get currently selected peer address
1272 NodeId id = nodes.at(i).data().toLongLong();
1273 // Find the node, disconnect it and clear the selected node
1274 if(m_node.disconnectById(id))
1276 }
1277}
1278
1280{
1281 if (!clientModel)
1282 return;
1283
1284 for (const QModelIndex& peer : GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId)) {
1285 // Find possible nodes, ban it and clear the selected node
1286 const auto stats = peer.data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>();
1287 if (stats) {
1288 m_node.ban(stats->nodeStats.addr, bantime);
1289 m_node.disconnectByAddress(stats->nodeStats.addr);
1290 }
1291 }
1294}
1295
1297{
1298 if (!clientModel)
1299 return;
1300
1301 // Get selected ban addresses
1302 QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address);
1303 for(int i = 0; i < nodes.count(); i++)
1304 {
1305 // Get currently selected ban address
1306 QString strNode = nodes.at(i).data().toString();
1307 CSubNet possibleSubnet;
1308
1309 LookupSubNet(strNode.toStdString(), possibleSubnet);
1310 if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet))
1311 {
1313 }
1314 }
1315}
1316
1318{
1319 ui->peerWidget->selectionModel()->clearSelection();
1320 cachedNodeids.clear();
1322}
1323
1325{
1326 if (!clientModel)
1327 return;
1328
1329 bool visible = clientModel->getBanTableModel()->shouldShow();
1330 ui->banlistWidget->setVisible(visible);
1331 ui->banHeading->setVisible(visible);
1332}
1333
1335{
1336 ui->tabWidget->setCurrentIndex(int(tabType));
1337}
1338
1339QString RPCConsole::tabTitle(TabTypes tab_type) const
1340{
1341 return ui->tabWidget->tabText(int(tab_type));
1342}
1343
1344QKeySequence RPCConsole::tabShortcut(TabTypes tab_type) const
1345{
1346 switch (tab_type) {
1347 case TabTypes::INFO: return QKeySequence(Qt::CTRL + Qt::Key_I);
1348 case TabTypes::CONSOLE: return QKeySequence(Qt::CTRL + Qt::Key_T);
1349 case TabTypes::GRAPH: return QKeySequence(Qt::CTRL + Qt::Key_N);
1350 case TabTypes::PEERS: return QKeySequence(Qt::CTRL + Qt::Key_P);
1351 } // no default case, so the compiler can warn about missing cases
1352
1353 assert(false);
1354}
1355
1356void RPCConsole::updateAlerts(const QString& warnings)
1357{
1358 this->ui->label_alerts->setVisible(!warnings.isEmpty());
1359 this->ui->label_alerts->setText(warnings);
1360}
#define PACKAGE_NAME
NodeContext m_node
Definition: bitcoin-gui.cpp:36
const CChainParams & Params()
Return the currently selected parameters.
bool IsValid() const
Model for Bitcoin network client.
Definition: clientmodel.h:48
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut)
QString blocksDir() const
QString getStatusBarWarnings() const
Return warnings to be displayed in status bar.
PeerTableModel * getPeerTableModel()
PeerTableSortProxy * peerTableSortProxy()
void numConnectionsChanged(int count)
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
QString formatClientStartupTime() const
int getNumConnections(unsigned int flags=CONNECTIONS_ALL) const
Return number of connections, default is in- and outbound (total)
Definition: clientmodel.cpp:77
BanTableModel * getBanTableModel()
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:55
static std::vector< std::string > ToStrings(NetPermissionFlags flags)
QString displayText(const QVariant &value, const QLocale &locale) const override
Definition: rpcconsole.cpp:143
PeerIdViewDelegate(QObject *parent=nullptr)
Definition: rpcconsole.cpp:140
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
bool getImagesOnButtons() const
Definition: platformstyle.h:21
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:109
std::function< void()> func
Definition: rpcconsole.cpp:122
QtRPCTimerBase(std::function< void()> &_func, int64_t millis)
Definition: rpcconsole.cpp:112
const char * Name() override
Implementation name.
Definition: rpcconsole.cpp:129
RPCTimerBase * NewTimer(std::function< void()> &func, int64_t millis) override
Factory function for timers.
Definition: rpcconsole.cpp:130
Local Bitcoin RPC console.
Definition: rpcconsole.h:39
QMenu * peersTableContextMenu
Definition: rpcconsole.h:164
RPCConsole(interfaces::Node &node, const PlatformStyle *platformStyle, QWidget *parent)
Definition: rpcconsole.cpp:471
struct RPCConsole::TranslatedStrings ts
void cmdRequest(const QString &command, const WalletModel *wallet_model)
void browseHistory(int offset)
Go forward or back in history.
QByteArray m_banlist_widget_header_state
Definition: rpcconsole.h:172
void fontSmaller()
Definition: rpcconsole.cpp:822
RPCTimerInterface * rpcTimerInterface
Definition: rpcconsole.h:163
void on_lineEdit_returnPressed()
Definition: rpcconsole.cpp:994
QStringList history
Definition: rpcconsole.h:158
void message(int category, const QString &msg)
Append the message to the message widget.
Definition: rpcconsole.h:109
void setFontSize(int newSize)
Definition: rpcconsole.cpp:827
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:173
const PlatformStyle *const platformStyle
Definition: rpcconsole.h:162
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:47
QString tabTitle(TabTypes tab_type) const
void updateNetworkState()
Update UI with latest network info from model.
Definition: rpcconsole.cpp:950
void clear(bool keep_prompt=false)
Definition: rpcconsole.cpp:852
void disconnectSelectedNode()
Disconnect a selected node on the Peers tab.
@ BANTIME_COLUMN_WIDTH
Definition: rpcconsole.h:151
@ ADDRESS_COLUMN_WIDTH
Definition: rpcconsole.h:147
@ SUBVERSION_COLUMN_WIDTH
Definition: rpcconsole.h:148
@ PING_COLUMN_WIDTH
Definition: rpcconsole.h:149
@ BANSUBNET_COLUMN_WIDTH
Definition: rpcconsole.h:150
QCompleter * autoCompleter
Definition: rpcconsole.h:167
void setMempoolSize(long numberOfTxs, size_t dynUsage)
Set size (number of transactions and memory usage) of the mempool in the UI.
Definition: rpcconsole.cpp:984
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:976
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:161
bool m_is_executing
Definition: rpcconsole.h:170
interfaces::Node & m_node
Definition: rpcconsole.h:155
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:166
void setNumConnections(int count)
Set number of connections shown in the UI.
Definition: rpcconsole.cpp:963
ClientModel * clientModel
Definition: rpcconsole.h:157
void banSelectedNode(int bantime)
Ban a selected node on the Peers tab.
int historyPtr
Definition: rpcconsole.h:159
void scrollToEnd()
Scroll console view to end.
void keyPressEvent(QKeyEvent *) override
Definition: rpcconsole.cpp:907
void on_tabWidget_currentChanged(int index)
Ui::RPCConsole *const ui
Definition: rpcconsole.h:156
void startExecutor()
void setNetworkActive(bool networkActive)
Set network state shown in the UI.
Definition: rpcconsole.cpp:971
void fontBigger()
Definition: rpcconsole.cpp:817
QString cmdBeforeBrowsing
Definition: rpcconsole.h:160
void addWallet(WalletModel *const walletModel)
virtual bool eventFilter(QObject *obj, QEvent *event) override
Definition: rpcconsole.cpp:604
void on_openDebugLogfileButton_clicked()
open the debug.log from the current datadir
void removeWallet(WalletModel *const walletModel)
void showBanTableContextMenu(const QPoint &point)
Show custom context menu on Bans tab.
void setClientModel(ClientModel *model=nullptr, int bestblock_height=0, int64_t bestblock_date=0, double verification_progress=0.0)
Definition: rpcconsole.cpp:649
void setTabFocus(enum TabTypes tabType)
set which tab has the focus (is visible)
QByteArray m_peer_widget_header_state
Definition: rpcconsole.h:171
void changeEvent(QEvent *e) override
Definition: rpcconsole.cpp:915
WalletModel * m_last_wallet_model
Definition: rpcconsole.h:169
void showOrHideBanTableIfRequired()
Hides ban table if no bans are present.
QString TimeDurationField(uint64_t time_now, uint64_t time_at_event) const
Helper for the output of a time duration field.
Definition: rpcconsole.h:178
QMenu * banTableContextMenu
Definition: rpcconsole.h:165
QThread thread
Definition: rpcconsole.h:168
void reply(int category, const QString &command)
RPCExecutor(interfaces::Node &node)
Definition: rpcconsole.cpp:93
interfaces::Node & m_node
Definition: rpcconsole.cpp:102
void request(const QString &command, const WalletModel *wallet_model)
Definition: rpcconsole.cpp:415
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:51
RPC timer "driver".
Definition: server.h:60
void setClientModel(ClientModel *model)
void setGraphRangeMins(int mins)
QLabel * peerHeight
QLabel * peerNetwork
QLabel * peerVersion
QLabel * dataDir
QLabel * peerConnectionTypeLabel
QLabel * peerLastRecv
QWidget * peersTabRightPanel
QLabel * peerLastBlock
QPushButton * openDebugLogfileButton
QToolButton * fontBiggerButton
QLabel * peerPermissions
QWidget * tab_console
TrafficGraphWidget * trafficGraph
QTableView * banlistWidget
QPushButton * promptIcon
QLabel * peerHighBandwidthLabel
QLabel * peerConnTime
QComboBox * WalletSelector
QLabel * peerLastSend
QLabel * numberOfConnections
QLabel * peerBytesSent
QLabel * peerSubversion
QLabel * mempoolSize
QLabel * peerRelayTxes
QLabel * peerPingTime
QLabel * blocksDir
QLabel * WalletSelectorLabel
QLabel * timeoffset
QPushButton * btnClearTrafficGraph
QLabel * peerCommonHeight
QLabel * banHeading
QLabel * clientUserAgent
QLabel * lblBytesOut
QLabel * numberOfBlocks
QLabel * peerMappedAS
QLineEdit * lineEdit
QLabel * peerMinPing
QLabel * peerLastTx
QLabel * peerHighBandwidth
QToolButton * fontSmallerButton
QLabel * clientVersion
QLabel * peerConnectionType
void setupUi(QWidget *RPCConsole)
QLabel * peerPingWait
QTableView * peerWidget
QLabel * peerBytesRecv
QToolButton * clearButton
QLabel * peerSyncHeight
QLabel * peerHeading
QLabel * lblGraphRange
QLabel * lblBytesIn
QTabWidget * tabWidget
QLabel * lastBlockTime
QLabel * startupTime
QLabel * networkName
QLabel * mempoolNumberTxs
QLabel * peerServices
QTextEdit * messagesWidget
QSplitter * splitter
QLabel * label_alerts
const std::string & get_str() const
bool isArray() const
Definition: univalue.h:81
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
bool isStr() const
Definition: univalue.h:79
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
bool isObject() const
Definition: univalue.h:82
int get_int() const
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:52
QString getDisplayName() const
static bool isWalletEnabled()
QString getWalletName() const
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:55
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:240
@ CONNECTIONS_IN
Definition: clientmodel.h:41
@ CONNECTIONS_OUT
Definition: clientmodel.h:42
QString NetworkToQString(Network net)
Convert enum Network to QString.
Definition: guiutil.cpp:664
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:233
QList< QModelIndex > getEntryData(const QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
Definition: guiutil.cpp:261
QFont fixedPitchFont(bool use_embedded_font)
Definition: guiutil.cpp:88
QString formatBytes(uint64_t bytes)
Definition: guiutil.cpp:791
void AddButtonShortcut(QAbstractButton *button, const QKeySequence &shortcut)
Connects an additional shortcut to a QAbstractButton.
Definition: guiutil.cpp:127
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:409
void copyEntryData(const QAbstractItemView *view, int column, int role)
Copy a field of the currently selected entry of a view to the clipboard.
Definition: guiutil.cpp:248
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:742
void openDebugLogfile()
Definition: guiutil.cpp:414
QString formatDurationStr(int secs)
Convert seconds into a QString with days, hours, mins, secs.
Definition: guiutil.cpp:708
QString formatTimeOffset(int64_t nTimeOffset)
Format a CNodeCombinedStats.nTimeOffset into a user-readable string.
Definition: guiutil.cpp:749
QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction)
Convert enum ConnectionType to QString.
Definition: guiutil.cpp:679
QString formatServicesStr(quint64 mask)
Format CNodeStats.nServices bitmask into a user-readable string.
Definition: guiutil.cpp:728
void ThreadRename(std::string &&)
Rename a thread both in terms of an internal (in-memory) name as well as its system thread name.
Definition: threadnames.cpp:57
int64_t NodeId
Definition: net.h:87
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:679
const std::vector< std::string > CONNECTION_TYPE_DOC
Definition: net.cpp:35
const int INITIAL_TRAFFIC_GRAPH_MINS
Definition: rpcconsole.cpp:57
const struct @8 ICON_MAPPING[]
const QSize FONT_RANGE(4, 40)
const int CONSOLE_HISTORY
Definition: rpcconsole.cpp:56
static QString categoryClass(int category)
Definition: rpcconsole.cpp:806
const char fontSizeSettingsKey[]
Definition: rpcconsole.cpp:59
const char * url
Definition: rpcconsole.cpp:62
const char * source
Definition: rpcconsole.cpp:63
auto Join(const std::vector< T > &list, const BaseType &separator, UnaryOp unary_op) -> decltype(unary_op(list.at(0)))
Join a list of items.
Definition: string.h:44
static int count
Definition: tests.c:41
int64_t GetTimeSeconds()
Returns the system time (not mockable)
Definition: time.cpp:127
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:236
assert(!tx.IsCoinBase())