Skip to content

Commit

Permalink
rpc: partially allow param conversions for composite commands
Browse files Browse the repository at this point in the history
The processing is done in RPCConvertValues() (RPC client) instead of
CRPCTable::execute() (RPC server) because we need to be able to populate
members(ByName) with arg positions without having to worry about the
offset-by-one as the parsing is done *before* execute() gets a chance
to account for the subcommand. Also because the parsing is done by the
client.

Named values are currently still not supported.
  • Loading branch information
kwvg committed Jul 10, 2024
1 parent f1635a9 commit 54c99fd
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 12 deletions.
52 changes: 41 additions & 11 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
class CRPCConvertParam
{
public:
std::string methodName; //!< method whose params want conversion
int paramIdx; //!< 0-based idx of param to convert
std::string paramName; //!< parameter name
std::string methodName; //!< method whose params want conversion
std::string methodSubName; //!< method subname whose params want conversion
int paramIdx; //!< 0-based idx of param to convert
std::string paramName; //!< parameter name

CRPCConvertParam(std::string _methodName, int _paramIdx, std::string _paramName) :
methodName{_methodName}, methodSubName{""}, paramIdx{_paramIdx}, paramName{_paramName} {}
CRPCConvertParam(std::string _methodName, std::string _methodSubName, int _paramIdx, std::string _paramName) :
methodName{_methodName}, methodSubName{_methodSubName}, paramIdx{_paramIdx}, paramName{_paramName} {}
};

// clang-format off
Expand Down Expand Up @@ -231,17 +237,29 @@ static const CRPCConvertParam vRPCConvertParams[] =
class CRPCConvertTable
{
private:
std::set<std::pair<std::string, int>> members;
std::set<std::pair<std::string, std::string>> membersByName;
std::set<std::pair<std::pair<std::string, std::string>, int>> members;
std::set<std::pair<std::pair<std::string, std::string>, std::string>> membersByName;

public:
CRPCConvertTable();

bool convert(const std::string& method, int idx) {
return (members.count(std::make_pair(method, idx)) > 0);
return (members.count(std::make_pair(std::make_pair(method, ""), idx)) > 0);
}
bool convert(const std::string& method, const std::string& submethod, int idx) {
return (members.count(std::make_pair(std::make_pair(method, submethod), idx)) > 0);
}
bool convert(const std::string& method, const std::string& name) {
return (membersByName.count(std::make_pair(method, name)) > 0);
return (membersByName.count(std::make_pair(std::make_pair(method, ""), name)) > 0);
}
bool convert(const std::string& method, const std::string& submethod, const std::string& name) {
return (membersByName.count(std::make_pair(std::make_pair(method, submethod), name)) > 0);
}
bool isSubCommand(const std::string& method, const std::string& submethod) {
for (const auto& [name, _] : members) {
if (!name.second.empty() && name == std::make_pair(method, submethod)) return true;
}
return false;
}
};

Expand All @@ -251,9 +269,9 @@ CRPCConvertTable::CRPCConvertTable()
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0]));

for (unsigned int i = 0; i < n_elem; i++) {
members.insert(std::make_pair(vRPCConvertParams[i].methodName,
members.insert(std::make_pair(std::make_pair(vRPCConvertParams[i].methodName, vRPCConvertParams[i].methodSubName),
vRPCConvertParams[i].paramIdx));
membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName,
membersByName.insert(std::make_pair(std::make_pair(vRPCConvertParams[i].methodName, vRPCConvertParams[i].methodSubName),
vRPCConvertParams[i].paramName));
}
}
Expand All @@ -272,14 +290,26 @@ UniValue ParseNonRFCJSONValue(const std::string& strVal)
return jVal[0];
}

UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
UniValue RPCConvertValues(const std::string& strMethod, std::vector<std::string> strParams)
{
UniValue params(UniValue::VARR);

// Check if strParams[0] is a subcommand. If it is, remove it from strParams so that
// the indexes of the arguments line up but still push it into params so that squashing
// logic down the line still works as expected.
std::string strSubCmd{""};
if (strParams.size() > 0 && strParams[0] != "") {
if (rpcCvtTable.isSubCommand(strMethod, strParams[0])) {
strSubCmd = strParams[0];
params.push_back(strSubCmd);
strParams.erase(strParams.begin());
}
}

for (unsigned int idx = 0; idx < strParams.size(); idx++) {
const std::string& strVal = strParams[idx];

if (!rpcCvtTable.convert(strMethod, idx)) {
if (!rpcCvtTable.convert(strMethod, strSubCmd, idx)) {
// insert string value directly
params.push_back(strVal);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <univalue.h>

/** Convert positional arguments to command-specific RPC representation */
UniValue RPCConvertValues(const std::string& strMethod, const std::vector<std::string>& strParams);
UniValue RPCConvertValues(const std::string& strMethod, std::vector<std::string> strParams);

/** Convert named arguments to command-specific RPC representation */
UniValue RPCConvertNamedValues(const std::string& strMethod, const std::vector<std::string>& strParams);
Expand Down
11 changes: 11 additions & 0 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,17 @@ std::vector<std::pair<std::string, std::string>> CRPCTable::listCommands() const
return commandList;
}

std::vector<std::string> CRPCTable::listSubCommands(const std::string& command) const
{
std::vector<std::string> ret;
for (const auto& [name, _] : mapCommands) {
if (const auto& [cmd, subcmd] = name; cmd == command) {
ret.emplace_back(subcmd);
}
}
return ret;
}

void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
{
if (!timerInterface)
Expand Down
6 changes: 6 additions & 0 deletions src/rpc/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ class CRPCTable
*/
std::vector<std::pair<std::string, std::string>> listCommands() const;

/**
* Returns a list of registered subcommands for a command
* @returns List of subcommands
*/
std::vector<std::string> listSubCommands(const std::string& command) const;

/**
* Appends a CRPCCommand to the dispatch table.
*
Expand Down
6 changes: 6 additions & 0 deletions test/lint/check-rpc-mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ def process_mapping(fname):
in_rpcs = False
elif '{' in line and '"' in line:
m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
# that's a quick fix for composite command
# no proper implementation is needed so far as this linter would be removed soon with bitcoin#20012
if not m:
m = re.search(r'{ *("[^"]*"), *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
if m:
continue
assert m, 'No match to table expression: %s' % line
name = parse_string(m.group(1))
idx = int(m.group(2))
Expand Down

0 comments on commit 54c99fd

Please sign in to comment.