Skip to content

Commit

Permalink
qt: add generate command to gui console
Browse files Browse the repository at this point in the history
  • Loading branch information
hernanmarino committed Jan 25, 2023
1 parent 8ab1923 commit ad0dd07
Showing 1 changed file with 116 additions and 43 deletions.
159 changes: 116 additions & 43 deletions src/qt/rpcconsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

#include <chrono>


const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
const QSize FONT_RANGE(4, 40);
Expand Down Expand Up @@ -96,6 +97,8 @@ public Q_SLOTS:

private:
interfaces::Node& m_node;
std::vector<std::string> parseHelper(const std::string& strCommand);
bool executeConsoleOnlyCommand(const std::string& command, const WalletModel* wallet_model);
};

/** Class for handling RPC timers
Expand Down Expand Up @@ -408,59 +411,129 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes
}
}

void RPCExecutor::request(const QString &command, const WalletModel* wallet_model)
/**
* Small and fast parser supporting console command syntax, with limited functionality.
* Splits a command line string into a vector with command at position 0, and parameters after
*
* @param[in] strCommand Command line to parse
*
* @return a vector of strings with command and parameters
*/

std::vector<std::string> RPCExecutor::parseHelper(const std::string& strCommand)
{
try
{
std::string result;
std::string executableCommand = command.toStdString() + "\n";
std::vector<std::string> vec = SplitString(strCommand, " (),\n");
auto should_remove = [](const std::string& str) { return str.empty(); };
vec.erase(std::remove_if(vec.begin(), vec.end(), should_remove), vec.end());
return vec;
}

// Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply.
if(executableCommand == "help-console\n") {
/**
* Catches console-only command before a RPC call is executed
*
* @param[in] command Command line to execute
* @param[in] wallet_model Wallet model to use
* @return true if command was handled by this method (even on errors), false otherwise
*
*/

bool RPCExecutor::executeConsoleOnlyCommand(const std::string& command, const WalletModel* wallet_model)
{
std::string result;
std::string address;
std::string executableCommand;
std::vector<std::string> parsedCommand{parseHelper(command)};

// Catch the console-only generate command with 2 or less parameters before RPC call is executed .
if (!parsedCommand.empty() && parsedCommand[0] == "generate" && parsedCommand.size() <= 3) {
// Default number of blocks is 1 if missing
const std::string nblocks{parsedCommand.size() > 1 ? parsedCommand[1] : "1"};
// An empty maxtries parameters will be modified by generatetoaddress' default value
const std::string maxtries{parsedCommand.size() > 2 ? parsedCommand[2] : ""};

// Generate address
if (!RPCConsole::RPCExecuteCommandLine(m_node, address, "getnewaddress\n", nullptr, wallet_model)) {
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Error: could not generate new address"));
} else {
executableCommand = "generatetoaddress " + nblocks + " \"" + address + "\" " + maxtries;
if (!RPCConsole::RPCExecuteCommandLine(m_node, result, "generatetoaddress " + nblocks + " " + address + " " + maxtries + "\n", nullptr, wallet_model)) {
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Error: could not generate blocks"));
} else {
std::string answer = "{\n \"address\": \"" + address + "\",\n \"blocks\": " + result + "\n}";
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString("\n" + answer + "\n\n"));
}
}
return true;
}
// Catch the console-only "help generate" command when requested, or when "generate is called with wrong parameters.
if ((parsedCommand.size() >= 2 && parsedCommand[0] == "help" && parsedCommand[1] == "generate") || (parsedCommand.size() > 3 && parsedCommand[0] == "generate")) {
Q_EMIT reply(RPCConsole::CMD_REPLY, QString(("\n"
"This console accepts RPC commands using the standard syntax.\n"
" example: getblockhash 0\n\n"
"Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress.\n"
"Optional integer arguments are number of blocks to generate and maximum iterations to try.\n"
"Equivalent to RPC generatetoaddress nblocks and maxtries arguments.\n"
" example: generate\n"
" example: generate 4\n"
" example: generate 3 6000\n\n")));

"This console can also accept RPC commands using the parenthesized syntax.\n"
" example: getblockhash(0)\n\n"
return true;
}
// Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply.
if (!parsedCommand.empty() && parsedCommand[0] == "help-console") {
Q_EMIT reply(RPCConsole::CMD_REPLY, QString(("\n"
"This console accepts RPC commands using the standard syntax.\n"
" example: getblockhash 0\n\n"

"Commands may be nested when specified with the parenthesized syntax.\n"
" example: getblock(getblockhash(0) 1)\n\n"
"This console can also accept RPC commands using the parenthesized syntax.\n"
" example: getblockhash(0)\n\n"

"A space or a comma can be used to delimit arguments for either syntax.\n"
" example: getblockhash 0\n"
" getblockhash,0\n\n"
"Commands may be nested when specified with the parenthesized syntax.\n"
" example: getblock(getblockhash(0) 1)\n\n"

"Named results can be queried with a non-quoted key string in brackets using the parenthesized syntax.\n"
" example: getblock(getblockhash(0) 1)[tx]\n\n"
"A space or a comma can be used to delimit arguments for either syntax.\n"
" example: getblockhash 0\n"
" getblockhash,0\n\n"

"Results without keys can be queried with an integer in brackets using the parenthesized syntax.\n"
" example: getblock(getblockhash(0),1)[tx][0]\n\n")));
return;
}
if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, wallet_model)) {
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
return;
}
"Named results can be queried with a non-quoted key string in brackets using the parenthesized syntax.\n"
" example: getblock(getblockhash(0) 1)[tx]\n\n"

Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
}
catch (UniValue& objError)
{
try // Nice formatting for standard-format error
{
int code = find_value(objError, "code").getInt<int>();
std::string message = find_value(objError, "message").get_str();
Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")");
}
catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message
{ // Show raw JSON object
Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(objError.write()));
}
"Results without keys can be queried with an integer in brackets using the parenthesized syntax.\n"
" example: getblock(getblockhash(0),1)[tx][0]\n\n")));
return true;
}
catch (const std::exception& e)
{
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what()));


// Returns false if no command was caught by previous code, so that it can be sent to RPC
return false;
}

void RPCExecutor::request(const QString& command, const WalletModel* wallet_model)
{
try {
std::string result;
std::string executableCommand = command.toStdString() + "\n";

// Attempt to execute console-only commands
if (!RPCExecutor::executeConsoleOnlyCommand(executableCommand, wallet_model)) {
// Send to the RPC command parser if not console-only
if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, wallet_model)) {
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
return;
}
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
}

} catch (UniValue& objError) {
try // Nice formatting for standard-format error
{
int code = find_value(objError, "code").getInt<int>();
std::string message = find_value(objError, "message").get_str();
Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")");
} catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message
{ // Show raw JSON object
Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(objError.write()));
}
} catch (const std::exception& e) {
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what()));
}
}

Expand Down

0 comments on commit ad0dd07

Please sign in to comment.