Changeset View
Changeset View
Standalone View
Standalone View
src/qt/rpcconsole.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#if defined(HAVE_CONFIG_H) | #if defined(HAVE_CONFIG_H) | ||||
#include "config/bitcoin-config.h" | #include "config/bitcoin-config.h" | ||||
#endif | #endif | ||||
#include "rpcconsole.h" | #include "rpcconsole.h" | ||||
#include "ui_debugwindow.h" | #include "ui_debugwindow.h" | ||||
#include "bantablemodel.h" | #include "qt/bantablemodel.h" | ||||
#include "clientmodel.h" | #include "qt/clientmodel.h" | ||||
#include "guiutil.h" | #include "qt/guiutil.h" | ||||
#include "platformstyle.h" | #include "qt/platformstyle.h" | ||||
#include "qt/walletmodel.h" | |||||
#include "chainparams.h" | #include "chainparams.h" | ||||
#include "config.h" | #include "config.h" | ||||
#include "netbase.h" | #include "netbase.h" | ||||
#include "rpc/client.h" | #include "rpc/client.h" | ||||
#include "rpc/server.h" | #include "rpc/server.h" | ||||
#include "util.h" | #include "util.h" | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
} // namespace | } // namespace | ||||
/* Object for executing console RPC commands in a separate thread. | /* Object for executing console RPC commands in a separate thread. | ||||
*/ | */ | ||||
class RPCExecutor : public QObject { | class RPCExecutor : public QObject { | ||||
Q_OBJECT | Q_OBJECT | ||||
public Q_SLOTS: | public Q_SLOTS: | ||||
void request(const QString &command); | void request(const QString &command, const QString &walletID); | ||||
Q_SIGNALS: | Q_SIGNALS: | ||||
void reply(int category, const QString &command); | void reply(int category, const QString &command); | ||||
}; | }; | ||||
/** Class for handling RPC timers | /** Class for handling RPC timers | ||||
* (used for e.g. re-locking the wallet after a timeout) | * (used for e.g. re-locking the wallet after a timeout) | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
* @param[in] fExecute set true if you want the command to be executed | * @param[in] fExecute set true if you want the command to be executed | ||||
* @param[out] pstrFilteredOut Command line, filtered to remove any sensitive | * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive | ||||
* data | * data | ||||
*/ | */ | ||||
bool RPCConsole::RPCParseCommandLine(std::string &strResult, | bool RPCConsole::RPCParseCommandLine(std::string &strResult, | ||||
const std::string &strCommand, | const std::string &strCommand, | ||||
const bool fExecute, | const bool fExecute, | ||||
std::string *const pstrFilteredOut) { | std::string *const pstrFilteredOut, | ||||
const std::string *walletID) { | |||||
std::vector<std::vector<std::string>> stack; | std::vector<std::vector<std::string>> stack; | ||||
stack.push_back(std::vector<std::string>()); | stack.push_back(std::vector<std::string>()); | ||||
enum CmdParseState { | enum CmdParseState { | ||||
STATE_EATING_SPACES, | STATE_EATING_SPACES, | ||||
STATE_EATING_SPACES_IN_ARG, | STATE_EATING_SPACES_IN_ARG, | ||||
STATE_EATING_SPACES_IN_BRACKETS, | STATE_EATING_SPACES_IN_BRACKETS, | ||||
STATE_ARGUMENT, | STATE_ARGUMENT, | ||||
▲ Show 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos) { | ||||
req.params = RPCConvertValues( | req.params = RPCConvertValues( | ||||
stack.back()[0], | stack.back()[0], | ||||
std::vector<std::string>( | std::vector<std::string>( | ||||
stack.back().begin() + 1, | stack.back().begin() + 1, | ||||
stack.back().end())); | stack.back().end())); | ||||
req.strMethod = stack.back()[0]; | req.strMethod = stack.back()[0]; | ||||
#ifdef ENABLE_WALLET | #ifdef ENABLE_WALLET | ||||
// TODO: Move this logic to WalletModel | if (walletID && !walletID->empty()) { | ||||
if (!vpwallets.empty()) { | |||||
// in Qt, use always the wallet with index 0 | |||||
// when running with multiple wallets | |||||
QByteArray encodedName = | QByteArray encodedName = | ||||
QUrl::toPercentEncoding( | QUrl::toPercentEncoding( | ||||
QString::fromStdString( | QString::fromStdString(*walletID)); | ||||
vpwallets[0]->GetName())); | |||||
req.URI = | req.URI = | ||||
"/wallet/" + | "/wallet/" + | ||||
std::string(encodedName.constData(), | std::string(encodedName.constData(), | ||||
encodedName.length()); | encodedName.length()); | ||||
} | } | ||||
#endif | #endif | ||||
GlobalConfig config; | GlobalConfig config; | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | switch (state) { | ||||
case STATE_ARGUMENT: | case STATE_ARGUMENT: | ||||
case STATE_EATING_SPACES: | case STATE_EATING_SPACES: | ||||
return true; | return true; | ||||
default: // ERROR to end in one of the other states | default: // ERROR to end in one of the other states | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
void RPCExecutor::request(const QString &command) { | void RPCExecutor::request(const QString &command, const QString &walletID) { | ||||
try { | try { | ||||
std::string result; | std::string result; | ||||
std::string executableCommand = command.toStdString() + "\n"; | std::string executableCommand = command.toStdString() + "\n"; | ||||
// Catch the console-only-help command before RPC call is executed and | // Catch the console-only-help command before RPC call is executed and | ||||
// reply with help text as-if a RPC reply. | // reply with help text as-if a RPC reply. | ||||
if (executableCommand == "help-console\n") { | if (executableCommand == "help-console\n") { | ||||
Q_EMIT reply( | Q_EMIT reply( | ||||
Show All 21 Lines | try { | ||||
" example: getblock(getblockhash(0) true)[tx]\n\n" | " example: getblock(getblockhash(0) true)[tx]\n\n" | ||||
"Results without keys can be queried using an integer " | "Results without keys can be queried using an integer " | ||||
"in brackets.\n" | "in brackets.\n" | ||||
" example: " | " example: " | ||||
"getblock(getblockhash(0),true)[tx][0]\n\n"))); | "getblock(getblockhash(0),true)[tx][0]\n\n"))); | ||||
return; | return; | ||||
} | } | ||||
if (!RPCConsole::RPCExecuteCommandLine(result, executableCommand)) { | std::string wallet_id = walletID.toStdString(); | ||||
if (!RPCConsole::RPCExecuteCommandLine(result, executableCommand, | |||||
nullptr, &wallet_id)) { | |||||
Q_EMIT reply(RPCConsole::CMD_ERROR, | Q_EMIT reply(RPCConsole::CMD_ERROR, | ||||
QString("Parse error: unbalanced ' or \"")); | QString("Parse error: unbalanced ' or \"")); | ||||
return; | return; | ||||
} | } | ||||
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result)); | Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result)); | ||||
} catch (UniValue &objError) { | } catch (UniValue &objError) { | ||||
// Nice formatting for standard-format error | // Nice formatting for standard-format error | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) | ||||
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); | connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); | ||||
connect(ui->fontBiggerButton, SIGNAL(clicked()), this, SLOT(fontBigger())); | connect(ui->fontBiggerButton, SIGNAL(clicked()), this, SLOT(fontBigger())); | ||||
connect(ui->fontSmallerButton, SIGNAL(clicked()), this, | connect(ui->fontSmallerButton, SIGNAL(clicked()), this, | ||||
SLOT(fontSmaller())); | SLOT(fontSmaller())); | ||||
connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, | connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, | ||||
SLOT(clear())); | SLOT(clear())); | ||||
// disable the wallet selector by default | |||||
ui->WalletSelector->setVisible(false); | |||||
ui->WalletSelectorLabel->setVisible(false); | |||||
// set library version labels | // set library version labels | ||||
#ifdef ENABLE_WALLET | #ifdef ENABLE_WALLET | ||||
ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0)); | ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0)); | ||||
#else | #else | ||||
ui->label_berkeleyDBVersion->hide(); | ui->label_berkeleyDBVersion->hide(); | ||||
ui->berkeleyDBVersion->hide(); | ui->berkeleyDBVersion->hide(); | ||||
#endif | #endif | ||||
// Register RPC timer interface | // Register RPC timer interface | ||||
▲ Show 20 Lines • Show All 239 Lines • ▼ Show 20 Lines | void RPCConsole::setClientModel(ClientModel *model) { | ||||
if (!model) { | if (!model) { | ||||
// Client model is being set to 0, this means shutdown() is about to be | // Client model is being set to 0, this means shutdown() is about to be | ||||
// called. Make sure we clean up the executor thread | // called. Make sure we clean up the executor thread | ||||
Q_EMIT stopExecutor(); | Q_EMIT stopExecutor(); | ||||
thread.wait(); | thread.wait(); | ||||
} | } | ||||
} | } | ||||
#ifdef ENABLE_WALLET | |||||
void RPCConsole::addWallet(WalletModel *const walletModel) { | |||||
const QString name = walletModel->getWalletName(); | |||||
// use name for text and internal data object (to allow to move to a wallet | |||||
// id later) | |||||
ui->WalletSelector->addItem(name, name); | |||||
if (ui->WalletSelector->count() == 2 && !isVisible()) { | |||||
// First wallet added, set to default so long as the window isn't | |||||
// presently visible (and potentially in use) | |||||
ui->WalletSelector->setCurrentIndex(1); | |||||
} | |||||
if (ui->WalletSelector->count() > 2) { | |||||
ui->WalletSelector->setVisible(true); | |||||
ui->WalletSelectorLabel->setVisible(true); | |||||
} | |||||
} | |||||
#endif | |||||
static QString categoryClass(int category) { | static QString categoryClass(int category) { | ||||
switch (category) { | switch (category) { | ||||
case RPCConsole::CMD_REQUEST: | case RPCConsole::CMD_REQUEST: | ||||
return "cmd-request"; | return "cmd-request"; | ||||
break; | break; | ||||
case RPCConsole::CMD_REPLY: | case RPCConsole::CMD_REPLY: | ||||
return "cmd-reply"; | return "cmd-reply"; | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | if (!cmd.isEmpty()) { | ||||
QString::fromStdString(e.what())); | QString::fromStdString(e.what())); | ||||
return; | return; | ||||
} | } | ||||
ui->lineEdit->clear(); | ui->lineEdit->clear(); | ||||
cmdBeforeBrowsing = QString(); | cmdBeforeBrowsing = QString(); | ||||
QString walletID; | |||||
#ifdef ENABLE_WALLET | |||||
const int wallet_index = ui->WalletSelector->currentIndex(); | |||||
if (wallet_index > 0) { | |||||
walletID = (QString)ui->WalletSelector->itemData(wallet_index) | |||||
.value<QString>(); | |||||
} | |||||
if (m_last_wallet_id != walletID) { | |||||
if (walletID.isEmpty()) { | |||||
message(CMD_REQUEST, | |||||
tr("Executing command without any wallet")); | |||||
} else { | |||||
message( | |||||
CMD_REQUEST, | |||||
tr("Executing command using \"%1\" wallet").arg(walletID)); | |||||
} | |||||
m_last_wallet_id = walletID; | |||||
} | |||||
#endif | |||||
message(CMD_REQUEST, QString::fromStdString(strFilteredCmd)); | message(CMD_REQUEST, QString::fromStdString(strFilteredCmd)); | ||||
Q_EMIT cmdRequest(cmd); | Q_EMIT cmdRequest(cmd, walletID); | ||||
cmd = QString::fromStdString(strFilteredCmd); | cmd = QString::fromStdString(strFilteredCmd); | ||||
// Remove command, if already in history | // Remove command, if already in history | ||||
history.removeOne(cmd); | history.removeOne(cmd); | ||||
// Append command to history | // Append command to history | ||||
history.append(cmd); | history.append(cmd); | ||||
// Enforce maximum history size | // Enforce maximum history size | ||||
Show All 32 Lines | |||||
void RPCConsole::startExecutor() { | void RPCConsole::startExecutor() { | ||||
RPCExecutor *executor = new RPCExecutor(); | RPCExecutor *executor = new RPCExecutor(); | ||||
executor->moveToThread(&thread); | executor->moveToThread(&thread); | ||||
// Replies from executor object must go to this object | // Replies from executor object must go to this object | ||||
connect(executor, SIGNAL(reply(int, QString)), this, | connect(executor, SIGNAL(reply(int, QString)), this, | ||||
SLOT(message(int, QString))); | SLOT(message(int, QString))); | ||||
// Requests from this object must go to executor | // Requests from this object must go to executor | ||||
connect(this, SIGNAL(cmdRequest(QString)), executor, | connect(this, SIGNAL(cmdRequest(QString, QString)), executor, | ||||
SLOT(request(QString))); | SLOT(request(QString, QString))); | ||||
// On stopExecutor signal | // On stopExecutor signal | ||||
// - quit the Qt event loop in the execution thread | // - quit the Qt event loop in the execution thread | ||||
connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit())); | connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit())); | ||||
// - queue executor for deletion (in execution thread) | // - queue executor for deletion (in execution thread) | ||||
connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()), | connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()), | ||||
Qt::DirectConnection); | Qt::DirectConnection); | ||||
▲ Show 20 Lines • Show All 331 Lines • Show Last 20 Lines |