diff --git a/src/interfaces/node.h b/src/interfaces/node.h --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -24,6 +24,8 @@ class HTTPRPCRequestProcessor; class proxyType; class RPCServer; +class RPCTimerInterface; +class UniValue; namespace interfaces { @@ -97,6 +99,16 @@ //! Get ban map entries. virtual bool getBanned(banmap_t &banmap) = 0; + //! Ban node. + virtual bool ban(const CNetAddr &net_addr, BanReason reason, + int64_t ban_time_offset) = 0; + + //! Unban node. + virtual bool unban(const CSubNet &ip) = 0; + + //! Disconnect node. + virtual bool disconnect(NodeId id) = 0; + //! Get total bytes recv. virtual int64_t getTotalBytesRecv() = 0; @@ -136,6 +148,20 @@ //! Get network active. virtual bool getNetworkActive() = 0; + //! Execute rpc command. + virtual UniValue executeRpc(Config &config, const std::string &command, + const UniValue ¶ms, + const std::string &uri) = 0; + + //! List rpc commands. + virtual std::vector listRpcCommands() = 0; + + //! Set RPC timer interface if unset. + virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) = 0; + + //! Unset RPC timer interface. + virtual void rpcUnsetTimerInterface(RPCTimerInterface *iface) = 0; + //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #endif #include +#include #include @@ -132,6 +134,27 @@ } return false; } + bool ban(const CNetAddr &net_addr, BanReason reason, + int64_t ban_time_offset) override { + if (g_connman) { + g_connman->Ban(net_addr, reason, ban_time_offset); + return true; + } + return false; + } + bool unban(const CSubNet &ip) override { + if (g_connman) { + g_connman->Unban(ip); + return true; + } + return false; + } + bool disconnect(NodeId id) override { + if (g_connman) { + return g_connman->DisconnectNode(id); + } + return false; + } int64_t getTotalBytesRecv() override { return g_connman ? g_connman->GetTotalBytesRecv() : 0; } @@ -184,6 +207,24 @@ bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); } + UniValue executeRpc(Config &config, const std::string &command, + const UniValue ¶ms, + const std::string &uri) override { + JSONRPCRequest req; + req.params = params; + req.strMethod = command; + req.URI = uri; + return ::tableRPC.execute(config, req); + } + std::vector listRpcCommands() override { + return ::tableRPC.listCommands(); + } + void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override { + RPCSetTimerInterfaceIfUnset(iface); + } + void rpcUnsetTimerInterface(RPCTimerInterface *iface) override { + RPCUnsetTimerInterface(iface); + } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage.connect(fn)); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -102,7 +102,7 @@ #endif setWindowTitle(windowTitle); - rpcConsole = new RPCConsole(_platformStyle, 0); + rpcConsole = new RPCConsole(node, _platformStyle, 0); helpMessageDialog = new HelpMessageDialog(node, this, false); #ifdef ENABLE_WALLET if (enableWallet) { diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -5,10 +5,10 @@ #ifndef BITCOIN_QT_RPCCONSOLE_H #define BITCOIN_QT_RPCCONSOLE_H -#include "guiutil.h" -#include "peertablemodel.h" +#include +#include -#include "net.h" +#include #include #include @@ -19,6 +19,10 @@ class RPCTimerInterface; class WalletModel; +namespace interfaces { +class Node; +} + namespace Ui { class RPCConsole; } @@ -33,20 +37,22 @@ Q_OBJECT public: - explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); + explicit RPCConsole(interfaces::Node &node, + const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); static bool - RPCParseCommandLine(std::string &strResult, const std::string &strCommand, - bool fExecute, + RPCParseCommandLine(interfaces::Node *node, std::string &strResult, + const std::string &strCommand, bool fExecute, std::string *const pstrFilteredOut = nullptr, const std::string *walletID = nullptr); static bool - RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, + RPCExecuteCommandLine(interfaces::Node &node, std::string &strResult, + const std::string &strCommand, std::string *const pstrFilteredOut = nullptr, const std::string *walletID = nullptr) { - return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut, - walletID); + return RPCParseCommandLine(&node, strResult, strCommand, true, + pstrFilteredOut, walletID); } void setClientModel(ClientModel *model); @@ -143,6 +149,7 @@ }; + interfaces::Node &m_node; Ui::RPCConsole *ui; ClientModel *clientModel; QStringList history; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -79,12 +79,17 @@ */ class RPCExecutor : public QObject { Q_OBJECT +public: + RPCExecutor(interfaces::Node &node) : m_node(node) {} public Q_SLOTS: void request(const QString &command, const QString &walletID); Q_SIGNALS: void reply(int category, const QString &command); + +private: + interfaces::Node &m_node; }; /** Class for handling RPC timers @@ -138,6 +143,7 @@ * - Within single quotes, no escaping is possible and no special * interpretation takes place * + * @param[in] node optional node to execute command on * @param[out] result stringified Result from the executed command(chain) * @param[in] strCommand Command line to split * @param[in] fExecute set true if you want the command to be executed @@ -145,7 +151,8 @@ * data */ -bool RPCConsole::RPCParseCommandLine(std::string &strResult, +bool RPCConsole::RPCParseCommandLine(interfaces::Node *node, + std::string &strResult, const std::string &strCommand, const bool fExecute, std::string *const pstrFilteredOut, @@ -323,28 +330,29 @@ // Convert argument list to JSON objects in // method-dependent way, and pass it along with // the method name to the dispatcher. - JSONRPCRequest req; - req.params = RPCConvertValues( + UniValue params = RPCConvertValues( stack.back()[0], std::vector( stack.back().begin() + 1, stack.back().end())); - req.strMethod = stack.back()[0]; + std::string method = stack.back()[0]; + std::string uri; #ifdef ENABLE_WALLET if (walletID && !walletID->empty()) { QByteArray encodedName = QUrl::toPercentEncoding( QString::fromStdString(*walletID)); - req.URI = - "/wallet/" + - std::string(encodedName.constData(), - encodedName.length()); + uri = "/wallet/" + + std::string(encodedName.constData(), + encodedName.length()); } #endif GlobalConfig config; - lastResult = tableRPC.execute(config, req); + assert(node); + lastResult = node->executeRpc(config, method, + params, uri); } state = STATE_COMMAND_EXECUTED; @@ -477,8 +485,8 @@ return; } std::string wallet_id = walletID.toStdString(); - if (!RPCConsole::RPCExecuteCommandLine(result, executableCommand, - nullptr, &wallet_id)) { + if (!RPCConsole::RPCExecuteCommandLine( + m_node, result, executableCommand, nullptr, &wallet_id)) { Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); return; @@ -505,9 +513,10 @@ } } -RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) - : QWidget(parent), ui(new Ui::RPCConsole), clientModel(0), historyPtr(0), - platformStyle(_platformStyle), peersTableContextMenu(0), +RPCConsole::RPCConsole(interfaces::Node &node, + const PlatformStyle *_platformStyle, QWidget *parent) + : QWidget(parent), m_node(node), ui(new Ui::RPCConsole), clientModel(0), + historyPtr(0), platformStyle(_platformStyle), peersTableContextMenu(0), banTableContextMenu(0), consoleFontSize(0) { ui->setupUi(this); QSettings settings; @@ -557,7 +566,7 @@ rpcTimerInterface = new QtRPCTimerInterface(); // avoid accidentally overwriting an existing, non QTThread // based timer interface - RPCSetTimerInterfaceIfUnset(rpcTimerInterface); + m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); @@ -573,7 +582,7 @@ RPCConsole::~RPCConsole() { QSettings settings; settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); - RPCUnsetTimerInterface(rpcTimerInterface); + m_node.rpcUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; } @@ -780,7 +789,7 @@ // Setup autocomplete and attach it QStringList wordList; - std::vector commandList = tableRPC.listCommands(); + std::vector commandList = m_node.listRpcCommands(); for (size_t i = 0; i < commandList.size(); ++i) { wordList << commandList[i].c_str(); wordList << ("help " + commandList[i]).c_str(); @@ -1013,7 +1022,7 @@ std::string strFilteredCmd; try { std::string dummy; - if (!RPCParseCommandLine(dummy, cmd.toStdString(), false, + if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) { // Failed to parse command, so we cannot even filter it for the // history @@ -1094,7 +1103,7 @@ } void RPCConsole::startExecutor() { - RPCExecutor *executor = new RPCExecutor(); + RPCExecutor *executor = new RPCExecutor(m_node); executor->moveToThread(&thread); // Replies from executor object must go to this object @@ -1342,10 +1351,6 @@ } void RPCConsole::disconnectSelectedNode() { - if (!g_connman) { - return; - } - // Get selected peer addresses QList nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); @@ -1353,12 +1358,14 @@ // Get currently selected peer address NodeId id = nodes.at(i).data().toLongLong(); // Find the node, disconnect it and clear the selected node - if (g_connman->DisconnectNode(id)) clearSelectedNode(); + if (m_node.disconnect(id)) { + clearSelectedNode(); + } } } void RPCConsole::banSelectedNode(int bantime) { - if (!clientModel || !g_connman) { + if (!clientModel) { return; } @@ -1380,8 +1387,7 @@ const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); if (stats) { - g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, - bantime); + m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime); } } clearSelectedNode(); @@ -1402,8 +1408,7 @@ CSubNet possibleSubnet; LookupSubNet(strNode.toStdString().c_str(), possibleSubnet); - if (possibleSubnet.IsValid() && g_connman) { - g_connman->Unban(possibleSubnet); + if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet)) { clientModel->getBanTableModel()->refresh(); } } diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -2,19 +2,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpcnestedtests.h" - -#include "chainparams.h" -#include "config.h" -#include "consensus/validation.h" -#include "fs.h" -#include "rpc/register.h" -#include "rpc/server.h" -#include "rpcconsole.h" -#include "test/test_bitcoin.h" -#include "univalue.h" -#include "util.h" -#include "validation.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -50,107 +51,115 @@ std::string result; std::string result2; std::string filtered; + auto node = interfaces::MakeNode(); // Simple result filtering with path. - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]", - &filtered); + RPCConsole::RPCExecuteCommandLine(*node, result, + "getblockchaininfo()[chain]", &filtered); QVERIFY(result == "main"); QVERIFY(filtered == "getblockchaininfo()[chain]"); // Simple 2 level nesting. - RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())"); + RPCConsole::RPCExecuteCommandLine(*node, result, + "getblock(getbestblockhash())"); RPCConsole::RPCExecuteCommandLine( - result, "getblock(getblock(getbestblockhash())[hash], true)"); + *node, result, "getblock(getblock(getbestblockhash())[hash], true)"); // 4 level nesting with whitespace, filtering path and boolean parameter. RPCConsole::RPCExecuteCommandLine( - result, "getblock( getblock( getblock(getbestblockhash())[hash] " - ")[hash], true)"); + *node, result, + "getblock( getblock( getblock(getbestblockhash())[hash] " + ")[hash], true)"); - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo"); + RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo"); QVERIFY(result.substr(0, 1) == "{"); - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()"); + RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()"); QVERIFY(result.substr(0, 1) == "{"); // Whitespace at the end will be tolerated. - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo "); + RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo "); QVERIFY(result.substr(0, 1) == "{"); // Quote path identifier are allowed, but look after a child contaning the // quotes in the key. - (RPCConsole::RPCExecuteCommandLine(result, + (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo()[\"chain\"]")); QVERIFY(result == "null"); // parameter not in brackets are allowed. - (RPCConsole::RPCExecuteCommandLine(result, "createrawtransaction [] {} 0")); + (RPCConsole::RPCExecuteCommandLine(*node, result, + "createrawtransaction [] {} 0")); // Parameter in brackets are allowed. - (RPCConsole::RPCExecuteCommandLine(result2, + (RPCConsole::RPCExecuteCommandLine(*node, result2, "createrawtransaction([],{},0)")); QVERIFY(result == result2); // Whitespace between parametres is allowed. (RPCConsole::RPCExecuteCommandLine( - result2, "createrawtransaction( [], {} , 0 )")); + *node, result2, "createrawtransaction( [], {} , 0 )")); QVERIFY(result == result2); RPCConsole::RPCExecuteCommandLine( - result, "getblock(getbestblockhash())[tx][0]", &filtered); + *node, result, "getblock(getbestblockhash())[tx][0]", &filtered); QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]"); - RPCConsole::RPCParseCommandLine(result, "importprivkey", false, &filtered); - QVERIFY(filtered == "importprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc", false, + RPCConsole::RPCParseCommandLine(nullptr, result, "importprivkey", false, &filtered); + QVERIFY(filtered == "importprivkey(…)"); + RPCConsole::RPCParseCommandLine( + nullptr, result, "signmessagewithprivkey abc", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def", - false, &filtered); + RPCConsole::RPCParseCommandLine( + nullptr, result, "signmessagewithprivkey abc,def", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signrawtransactionwithkey(abc)", - false, &filtered); + RPCConsole::RPCParseCommandLine( + nullptr, result, "signrawtransactionwithkey(abc)", false, &filtered); QVERIFY(filtered == "signrawtransactionwithkey(…)"); - RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false, - &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrase(help())", + false, &filtered); QVERIFY(filtered == "walletpassphrase(…)"); RPCConsole::RPCParseCommandLine( - result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", - false, &filtered); + nullptr, result, + "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, + &filtered); QVERIFY(filtered == "walletpassphrasechange(…)"); - RPCConsole::RPCParseCommandLine(result, "help(encryptwallet(abc, def))", - false, &filtered); + RPCConsole::RPCParseCommandLine( + nullptr, result, "help(encryptwallet(abc, def))", false, &filtered); QVERIFY(filtered == "help(encryptwallet(…))"); - RPCConsole::RPCParseCommandLine(result, "help(importprivkey())", false, - &filtered); - QVERIFY(filtered == "help(importprivkey(…))"); - RPCConsole::RPCParseCommandLine(result, "help(importprivkey(help()))", + RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey())", false, &filtered); QVERIFY(filtered == "help(importprivkey(…))"); RPCConsole::RPCParseCommandLine( - result, "help(importprivkey(abc), walletpassphrase(def))", false, - &filtered); + nullptr, result, "help(importprivkey(help()))", false, &filtered); + QVERIFY(filtered == "help(importprivkey(…))"); + RPCConsole::RPCParseCommandLine( + nullptr, result, "help(importprivkey(abc), walletpassphrase(def))", + false, &filtered); QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest"); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest"); QVERIFY(result == "[]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest ''"); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest ''"); QVERIFY(result == "[\"\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest \"\""); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest \"\""); QVERIFY(result == "[\"\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest '' abc"); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest '' abc"); QVERIFY(result == "[\"\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc '' abc"); + RPCConsole::RPCExecuteCommandLine(*node, result, + "rpcNestedTest abc '' abc"); QVERIFY(result == "[\"abc\",\"\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc abc"); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc abc"); QVERIFY(result == "[\"abc\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc\t\tabc"); + RPCConsole::RPCExecuteCommandLine(*node, result, + "rpcNestedTest abc\t\tabc"); QVERIFY(result == "[\"abc\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc )"); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc )"); QVERIFY(result == "[\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc )"); + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc )"); QVERIFY(result == "[\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, + RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc , cba )"); QVERIFY(result == "[\"abc\",\"cba\"]"); @@ -159,41 +168,42 @@ // (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3) // invalid syntax - QVERIFY_EXCEPTION_THROWN( - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() .\n"), - std::runtime_error); + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( + *node, result, "getblockchaininfo() .\n"), + std::runtime_error); // invalid syntax QVERIFY_EXCEPTION_THROWN( RPCConsole::RPCExecuteCommandLine( - result, "getblockchaininfo() getblockchaininfo()"), + *node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); // tolerate non closing brackets if we have no arguments. - (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(")); + (RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo(")); // tolerate non command brackts - (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()()()")); + (RPCConsole::RPCExecuteCommandLine(*node, result, + "getblockchaininfo()()()")); // invalid argument - QVERIFY_EXCEPTION_THROWN( - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(True)"), - UniValue); + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( + *node, result, "getblockchaininfo(True)"), + UniValue); // method not found - QVERIFY_EXCEPTION_THROWN( - RPCConsole::RPCExecuteCommandLine(result, "a(getblockchaininfo(True))"), - UniValue); + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( + *node, result, "a(getblockchaininfo(True))"), + UniValue); // don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN( - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc,,abc"), - std::runtime_error); + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( + *node, result, "rpcNestedTest abc,,abc"), + std::runtime_error); // don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN( - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), - std::runtime_error); + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( + *node, result, "rpcNestedTest(abc,,abc)"), + std::runtime_error); // don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN( - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), - std::runtime_error); + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine( + *node, result, "rpcNestedTest(abc,,)"), + std::runtime_error); #endif fs::remove_all(fs::path(path));