-
Notifications
You must be signed in to change notification settings - Fork 37.7k
rpc: Add RPCContext #20017
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: Add RPCContext #20017
Conversation
Here's more goals of this change:
|
24654fc
to
c6ecc35
Compare
Concept ACK I think this looks nicer btw: kallewoof@fe349cf |
@kallewoof thanks for taking a look! I'll wait for more concept acks before going forward. Regarding your suggestion to use |
Yeah, it may be confusing too. I do think we can afford to use one function that takes a null default, though. Overhead negligible. Gotcha on the concept ACKs. Edit: alternatively, Edit again: I dug some more, and it doesn't seem like the |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
1a07390
to
871487c
Compare
Updated with IMO a better API. @ryanofsky care to take a look too? |
871487c
to
a25df4d
Compare
@promag Nice. I can't figure out what was wrong with the old code. Care to explain? |
Before RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCLegacyMethodImpl fun)
: RPCHelpMan(std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), [&](const RPCContext& ctx) -> UniValue {
return fun(ctx.m_helpman, ctx.m_request);
})
{} |
Concept ACK. Would be nice if the default was already filled in by rpc help man. ;) |
@MarcoFalke yes, it was already suggested in #20017 (comment) but I think that can be done next. I also wonder if this should be an exhaustive change, at the moment I'm inclined to do it. |
The benefit of doing it in one step would be to avoid changing the same line of code twice |
01a1554
to
95453d7
Compare
See last commit @MarcoFalke. |
Rebased now that #21679 is merged. Not sure what is the best approach going forward, whether this should be an exhaustive change or not. I've included a commit to just change |
97bc6af
to
f473a17
Compare
Concept ACK |
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.
reACK f473a17
reACK f473a17 |
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
src/rpc/util.h
Outdated
class RPCContext | ||
{ | ||
public: | ||
const RPCHelpMan& helpman; | ||
const JSONRPCRequest& request; |
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.
6204785: It looks like you are using this like a struct (without m_
prefix and scoped access ctx.request
), so it could make sense to make it a struct. Also, with RPCHelpMan
being renamed to RPCMethod
(#19386 (comment)), it could make sese to name the variable appropriately:
class RPCContext | |
{ | |
public: | |
const RPCHelpMan& helpman; | |
const JSONRPCRequest& request; | |
struct RPCContext { | |
const RPCHelpMan& method; | |
const JSONRPCRequest& request; |
src/rpc/util.h
Outdated
CHECK_NONFATAL(m_arg.m_fallback.index() != 2); | ||
m_defaults = defaults; | ||
return *this; | ||
} |
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.
I don't understand how any of this works. I removed this and everything compiled just fine:
diff --git a/src/rpc/util.h b/src/rpc/util.h
index f7a1acd1b5..1d5b7fc318 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -348,11 +348,6 @@ struct RPCParam
const RPCArg& m_arg;
const UniValue& m_value;
std::optional<T> m_defaults = {};
- RPCParam<T>& defaults(const T& defaults) {
- CHECK_NONFATAL(m_arg.m_fallback.index() != 2);
- m_defaults = defaults;
- return *this;
- }
T get() const {
if (!m_value.isNull()) return value(m_value);
if (m_defaults) return *m_defaults;
I think it would make sense to split this into at least two commits:
- First one to introduce
RPCContext
- Second one to introduce
RPCParam
Finally, would be nice to use clang-format on new code.
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.
https://github.com/bitcoin/bitcoin/pull/20017/files#r682710976
@MarcoFalke yup, not used in this branch.
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.
Then maybe remove the dead, (not even compiled) code?
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.
@promag are you still working on this?
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.
I’ll rebase and remove dead code.
f473a17
to
7a7b050
Compare
@MarcoFalke updated, I think fail is unrelated. |
Concept ACK |
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.
Approach ACK
bool isNum() const { return value.isNum(); } | ||
bool isStr() const { return value.isStr(); } | ||
template <typename F> | ||
void forEach(F fn) const { |
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.
Any reason not to use UpperCamelCase?
} | ||
} | ||
private: | ||
T convert(const UniValue& value) const; |
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.
Parameter name shadows member variable. Shouldn't this method be static anyway?
T get() const { | ||
if (!value.isNull()) return convert(value); | ||
// if (m_defaults) return *m_defaults; | ||
CHECK_NONFATAL(arg.m_fallback.index() == 2); |
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.
CHECK_NONFATAL(std::holds_alternative<Default>(arg.m_fallback))
?
@@ -365,6 +395,25 @@ class RPCHelpMan | |||
const std::vector<RPCArg> m_args; | |||
const RPCResults m_results; | |||
const RPCExamples m_examples; | |||
friend struct RPCContext; |
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.
Rather than having a friend accessing this class's privates directly, why not have RPCHelpMan be able to create a parameter directly and just call that from RPCContext:
class RPCHelpMan
{
...
template <typename T>
RPCParam<T> GetParam(size_t index, const JSONRPCRequest& request) const
{
return {m_args[index], request.params[index]};
}
...
};
class RPCContext
{
...
template <typename T=UniValue>
RPCParam<T> param(size_t index) const
{
return method.GetParam<T>(index, request);
}
};
template <typename T=UniValue> | ||
RPCParam<T> param(size_t index) const | ||
{ | ||
return {method.m_args[index], request.params[index]}; |
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.
CHECK_NONFATAL(index < m_args.size());
?
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.
When constructing an RPCParam<T>
wouldn't it be worth checking that T
aligns with method.m_args[index].m_type
along similar lines to RPCResult::MatchesType
?
auto param(size_t index, F fn) const -> decltype(fn(UniValue{})) | ||
{ | ||
return fn(request.params[index]); | ||
} |
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.
Why would you call ctx.param(i, fn)
instead of fn(ctx.param(i))
-- ctx.param(i)
defaults to being UniValue and automatically converts to UniValue, no?
const RPCArg& arg; | ||
const UniValue& value; | ||
T get() const { | ||
if (!value.isNull()) return convert(value); |
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.
if constexpr (std::is_same_v<T, UniValue>()) return convert(value);
would allow you to get a VNULL
value out cleanly if you're treating the param as UniValue
🐙 This pull request conflicts with the target branch and needs rebase. Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft". |
Are you still working on this? |
I took the concept from here (Thanks!), but implemented it differently (in just three lines of code in the header file). See #28230 |
bee56c7 rpc: Check default value type againts argument type (João Barbosa) f81ef43 rpc: Keep default argument value in correct type (João Barbosa) Pull request description: Store default values of RPC arguments in the corresponding type instead of a string. The value is then serialized when the help output is needed. This change simplifies bitcoin#20017. The following examples illustrates how to use the new `RPCArg::Default` and `RPCArg::DefaultHint`: ```diff - {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"} + {"verbose", RPCArg::Type::BOOL, RPCArg::Default(false), "True for a json object, false for array of transaction ids"} ``` ```diff - {"nblocks", RPCArg::Type::NUM, /* default */ "one month", "Size of the window in number of blocks"} + {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint("one month"), "Size of the window in number of blocks"} ``` No behavior change is expected. ACKs for top commit: LarryRuane: ACK bee56c7 MarcoFalke: ACK bee56c7 🦅 Tree-SHA512: c47d78c918e996d36631d4ad3c933b270a34c5b446b8d736be94cf4a0a7b8c0e33d954149ec786cf9550639865b79deb6a130ad044de6030f95aac33f524293a
bee56c7 rpc: Check default value type againts argument type (João Barbosa) f81ef43 rpc: Keep default argument value in correct type (João Barbosa) Pull request description: Store default values of RPC arguments in the corresponding type instead of a string. The value is then serialized when the help output is needed. This change simplifies bitcoin#20017. The following examples illustrates how to use the new `RPCArg::Default` and `RPCArg::DefaultHint`: ```diff - {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"} + {"verbose", RPCArg::Type::BOOL, RPCArg::Default(false), "True for a json object, false for array of transaction ids"} ``` ```diff - {"nblocks", RPCArg::Type::NUM, /* default */ "one month", "Size of the window in number of blocks"} + {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint("one month"), "Size of the window in number of blocks"} ``` No behavior change is expected. ACKs for top commit: LarryRuane: ACK bee56c7 MarcoFalke: ACK bee56c7 🦅 Tree-SHA512: c47d78c918e996d36631d4ad3c933b270a34c5b446b8d736be94cf4a0a7b8c0e33d954149ec786cf9550639865b79deb6a130ad044de6030f95aac33f524293a
bee56c7 rpc: Check default value type againts argument type (João Barbosa) f81ef43 rpc: Keep default argument value in correct type (João Barbosa) Pull request description: Store default values of RPC arguments in the corresponding type instead of a string. The value is then serialized when the help output is needed. This change simplifies bitcoin#20017. The following examples illustrates how to use the new `RPCArg::Default` and `RPCArg::DefaultHint`: ```diff - {"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"} + {"verbose", RPCArg::Type::BOOL, RPCArg::Default(false), "True for a json object, false for array of transaction ids"} ``` ```diff - {"nblocks", RPCArg::Type::NUM, /* default */ "one month", "Size of the window in number of blocks"} + {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint("one month"), "Size of the window in number of blocks"} ``` No behavior change is expected. ACKs for top commit: LarryRuane: ACK bee56c7 MarcoFalke: ACK bee56c7 🦅 Tree-SHA512: c47d78c918e996d36631d4ad3c933b270a34c5b446b8d736be94cf4a0a7b8c0e33d954149ec786cf9550639865b79deb6a130ad044de6030f95aac33f524293a
This change allows to do
[&](const RPCContext& ctx) -> UniValue
instead of
So
RPCContext
tiesRPCHelpMan
andJSONRPCRequest
. Thenctx
is used to get actual parameter values. For instance, before:and now
Not that the default value defined in the RPC spec is used.
It is also possible to iterate an array parameter:
Or even do custom parameter handling: