-
Notifications
You must be signed in to change notification settings - Fork 37.7k
RPC: Internal named params #11660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RPC: Internal named params #11660
Conversation
Haven't looked at the code to deep. Using name based arguments rather then arguments by index would increase readability a lot (and we have the position already in the RPCTable). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice change. @promag, you might want to look at this. You were asking about request.params.size() checks everywhere to trigger help (https://botbot.me/freenode/bitcoin-core-dev/msg/93498239/), and this PR gets rid of the need for these with transformPositionalArguments
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK. Should be a lot friendly to query parameters by name instead. My suggestion is to take the opportunity and somehow also improve the internal API.
return out; | ||
} | ||
|
||
static inline JSONRPCRequest transformArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames, const bool want_named_args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arg_names
.
@@ -19,6 +20,8 @@ | |||
|
|||
static const unsigned int DEFAULT_RPC_SERIALIZE_VERSION = 1; | |||
|
|||
static const std::string ARGS_WERE_POSITIONAL = "_positional"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove?
return out; | ||
} | ||
out.params = UniValue(UniValue::VOBJ); | ||
out.params.pushKV(ARGS_WERE_POSITIONAL, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove or add usage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's intended for #11413
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How? Should be added there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should probably fail if a named-args caller specifies conf_target
with the explicit fee mode, or if feerate
is specified with the automatic fee modes, so it would need to check if the duplication is merely because of positional arguments being used or not.
@@ -3082,24 +3082,22 @@ UniValue bumpfee(const JSONRPCRequest& request) | |||
HelpExampleCli("bumpfee", "<txid>")); | |||
} | |||
|
|||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ}); | |||
RPCTypeCheckObj(request.params, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO RPCTypeCheckObj
is not very elegant. Here argument type is checked, and below it is parsed (which can fail) and sometimes it is validated (for instance out of bounds). Not to mention the "argument is not required and can be null" cases.
Something would be cleaner (don't mind the API):
uint256 hash = RequestParamUint256(request, "txid");
And if it has a default argument then it is not required:
bool replaceable = RequestParamBool(request, "options.replaceable", true);
So the idea is that these RequestParam*
calls check, parse and validate the argument.
This is obviously work for other PR, but if you drop the options magic transformation and UniValueType
changes then there is no change here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would we detect unexpected params?
I've had a quick skim and this looks like a good, clean change that maintains backwards-compatibility. @luke-jr, just so I understand the reasoning here, can you explain what the longer-term plan is:
|
Positional arguments are still fully supported. (No "backward" about it...)
Right
That's what this does already...?
I think it's best to have as much of the stuff for a given RPC with it in the code. We already have this for most things. |
Perhaps I was unclear - is the plan to remove the 'options' argument in the help text so that the 'standard' way to call the RPC is with the options as key-values in the request rather than in an options object in the request? |
I'm not sure. The help text covers both named and positional arguments, and an options Object is the ideal way to do many things with positional arguments. |
src/wallet/rpcwallet.cpp
Outdated
@@ -3310,7 +3308,7 @@ static const CRPCCommand commands[] = | |||
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, | |||
{ "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, | |||
{ "wallet", "backupwallet", &backupwallet, {"destination"} }, | |||
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, | |||
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"}, true }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to have this new "named_args" bool field. Univalue's array access operator explicitly works on object values as well as array values:
bitcoin/src/univalue/lib/univalue.cpp
Lines 211 to 219 in 99bc0b4
const UniValue& UniValue::operator[](size_t index) const | |
{ | |
if (typ != VOBJ && typ != VARR) | |
return NullUniValue; | |
if (index >= values.size()) | |
return NullUniValue; | |
return values.at(index); | |
} |
So instead of introducing different flavors of RPC functions where some receive array params and others receive object params, every RPC function could just receive the same params, and choose to access arguments by name or position, whichever is more convenient. Implementing this would also simplify transformNamedArguments
which could get rid of the "hole-filling" logic no longer needed after #11050.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, interesting thought. It feels a bit too much like relying on undefined behaviour to me. It's also probably complicated to implement properly - so before I put a bunch of effort into that: What do others think?
Considering the bool is only temporary anyway (I can complete the conversion in a single PR?), is this worth it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering the bool is only temporary anyway (I can complete the conversion in a single PR?), is this worth it?
Oh, I would say definitely not worth it if you are planning on getting rid of positional accesses entirely (and deleting transformNamedArguments).
It feels a bit too much like relying on undefined behaviour to me.
I can see how this "feels" like relying on undefined behavior, but of course it is not really. It is just relying on univalue behavior that's outside the realm on the json spec. In some cases where we do this (e.g. interpretation of numeric values, not distinguishing between undefined and null values) this isn't great because it can lead to incompatibility with client json libraries. But in this case we're just talking about passing params from our internal rpc framework to our internal rpc method implementations, so using univalue to pass on position metadata should not be a big deal. Again, though not worth it if you are planning on getting rid of positional accesses.
2ca71c0
to
83d1613
Compare
Rebased... |
There hasn't been much activity lately and the patch still needs rebase, so I am closing this for now. Please let me know when you want to continue working on this, so the pull request can be re-opened. |
This allows RPC code to use named parameters internally, greatly increasing readability, as well as helping avoid behaviour tied to param count rather than the presence of specific parameters.
Object type checking is expanded to support multiple allowed types, making param and type-checking clean.
Temporarily, a boolean is added to the end of CRPCCommand to indicate whether the function expects named params. Once all RPC functions have been converted, we can drop it (as well as old internal-positional-param code).
Alternative to #11441