Skip to content
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: Assert that RPCArg names are equal to CRPCCommand ones (server) #19386

Merged
merged 5 commits into from
Jul 15, 2020

Conversation

maflcko
Copy link
Member

@maflcko maflcko commented Jun 26, 2020

This is split out from #18531 to just touch the RPC methods in server. Description from the main pr:

Motivation

RPCArg names in the rpc help are currently only used for documentation. However, in the future they could be used to teach the server the named arguments. Named arguments are currently registered by the CRPCCommands and duplicate the RPCArg names from the documentation. This redundancy is fragile, and has lead to errors in the past (despite having linters to catch those kind of errors). See section "bugs found" for a list of bugs that have been found as a result of the changes here.

Changes

The changes here add an assert in the CRPCCommand constructor that the RPCArg names are identical to the ones in the CRPCCommand.

Future work

Here or follow up, makes sense to also assert type of returned UniValue?

Sure, but let's not get ahead of ourselves. I am going to submit any further works as follow-ups, including:

  • Removing the CRPCCommand arguments, now that they are asserted to be equal and thus redundant
  • Removing all python regex linters on the args, now that RPCMan can be used to generate any output, including the cli.cpp table
  • Auto-formatting and sanity checking the RPCExamples with RPCMan
  • Checking passed-in json in self-check. Removing redundant checks
  • Checking returned json against documentation to avoid regressions or false documentation
  • Compile the RPC documentation at compile-time to ensure it doesn't change at runtime and is completely static

Bugs found

@DrahtBot
Copy link
Contributor

DrahtBot commented Jun 26, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, 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.

@laanwj
Copy link
Member

laanwj commented Jun 29, 2020

Concept ACK.

Here or follow up, makes sense to also assert type of returned UniValue?

FWIW I dislike the idea of doing this at run-time. We don't want RPC to suddenly crash because an argument doesn't match its specified type in the help.
The extra checking also adds overhead on every RPC call.
It might make sense for something that is enabled with other heavier debug checks, enabled during the tests.

Removing all python regex linters on the args, now that RPCMan can be used to generate any output, including the cli.cpp table

This is somewhat difficult. It was considered in the past but

  • It would be undesirable to link the server-side tables into the CLI, as this either pulls in the associated implementations or splits the table in yet another way.
  • The CLI table could be spit out by some command line flag to bitcoin, however because of cross-compilation we don't want the build process to depend on being able to run the generated executable (and it needs to be possible to build -cli without -d).
  • bitcoin-cli could stop hardcoding the table completely but request it, over the network, from bitcoind. This is the most elegant option in a certain regard, but, one would want to cache this table (and not load it on every request). Getting the cache invalidation strategy (when changes) right is non-trivia,l though.

@maflcko
Copy link
Member Author

maflcko commented Jun 29, 2020

It might make sense for something that is enabled with other heavier debug checks, enabled during the tests.

Good idea. This could be hidden behind --enable-debug or some other option

The CLI table could be spit out by some command line flag to bitcoin, however because of cross-compilation we don't want the build process to depend on being able to run the generated executable (and it needs to be possible to build -cli without -d).

Makes sense. I think I will keep the linter/test around, but make it less fragile by removing the regex parsing, which has been broken in the past at least once.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fac2edb. This is a great way of getting rid of redundant RPC metadata without requiring invasive changes. Also thanks from splitting this out from the more overwhelmingly sized #18531.

re: #19386 (comment)

FWIW I dislike the idea of doing this at run-time

I think it's important to distinguish three types of checking

  1. Build time checking
  2. Run time checking in unit tests or at startup
  3. Run time checking at every method call

I think 1 & 2 are both perfectly good places to catch wrongly declared RPC methods. Only 3 is a bad place.

src/rpc/util.h Outdated Show resolved Hide resolved
src/rpc/util.h Outdated Show resolved Hide resolved
src/rpc/server.cpp Outdated Show resolved Hide resolved
src/rpc/util.h Outdated
@@ -329,6 +329,11 @@ class RPCHelpMan
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
using RPCMethod = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
struct HiddenArg {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit " rpc: Assert that passed arg names are equal to hardcoded ones (server)" (fac2edb)

It seems like it might be cleaner if instead of adding a new HiddenArg struct, and new RPCHelpMan constructor and special case code for m_hidden_arg in GetArgNames, this just added a new RPCArg::Type::HIDDEN type. This would also support multiple hidden args, or hidden args after non-hidden args.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hidden args should not be a thing, because I can't imagine a use case for them.

If they are needed for testing, a new (hidden) RPC method with un-hidden RPC arguments should be added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #19386 (comment)

Hidden args should not be a thing, because I can't imagine a use case for them.

If they are needed for testing, a new (hidden) RPC method with un-hidden RPC arguments should be added.

Beyond testing, undocumented hidden args also seem useful as a way of removing options we no longer want to support like wallet account and min_conf arguments in some places, and still maintaining compatibility when arguments are set to specific values (e.g. min_conf=1, account="*").

But regardless of whether hidden args will be used in the future, adding a Type::HIDDEN enum value seems like a simpler way to implement hidden args now than adding a HiddenArg struct and alternate code path.

Copy link
Member Author

@maflcko maflcko Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beyond testing, undocumented hidden args also seem useful as a way of removing options

I disagree. The RPC interface seems the wrong place to hide options in the documentation but then still accept them. For deprecation we use -rpcdeprecated and proper updates to the documentation to say an argument has been deprecated or is discouraged. (Edit: Making an option hidden will only hide the documentation that says the argument has been deprecated and thus will have the opposite effect of what was intended, no?)

I plan to remove HiddenArg before the next release, so the implementation shouldn't matter as long as it is minimal and correct.

Copy link
Member

@promag promag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK.

This allows the constructor to ask the rpc manager for the name of the
rpc method or the rpc argument names instead of having it manually
passed in.
@maflcko maflcko force-pushed the 2006-rpcManServer branch 3 times, most recently from fa7276a to fa87adf Compare June 30, 2020 18:32
@maflcko
Copy link
Member Author

maflcko commented Jun 30, 2020

Addressed feedback. Sorry for the intermediate force-pushes

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fa87adf. Changes since previous review: removing redundant throw, renaming some things, adding RPCHelpMan::HandleRequest method to replace macro, changing commit order.

So suggestion from #19386 (comment) doesn't get lost, I think HandleRequest(request) method could call Check(request), so every individual method doesn't have to check its own arguments.

src/rpc/util.h Outdated
@@ -329,6 +329,11 @@ class RPCHelpMan
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
using RPCMethod = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
struct HiddenArg {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #19386 (comment)

Hidden args should not be a thing, because I can't imagine a use case for them.

If they are needed for testing, a new (hidden) RPC method with un-hidden RPC arguments should be added.

Beyond testing, undocumented hidden args also seem useful as a way of removing options we no longer want to support like wallet account and min_conf arguments in some places, and still maintaining compatibility when arguments are set to specific values (e.g. min_conf=1, account="*").

But regardless of whether hidden args will be used in the future, adding a Type::HIDDEN enum value seems like a simpler way to implement hidden args now than adding a HiddenArg struct and alternate code path.

MarcoFalke added 2 commits July 3, 2020 11:08
Also, update switch statments to our style guide
Also, move Check to inside HandleRequest
@maflcko
Copy link
Member Author

maflcko commented Jul 3, 2020

So suggestion from #19386 (comment) doesn't get lost, I think HandleRequest(request) method could call Check(request), so every individual method doesn't have to check its own arguments.

Thanks. Fixed and force pushed

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fa7592b. Looks great! Just some hidden arg and Check() and comment cleanups since last review

@laanwj
Copy link
Member

laanwj commented Jul 15, 2020

ACK fa7592b

Makes sense. I think I will keep the linter/test around, but make it less fragile by removing the regex parsing, which has been broken in the past at least once.

I agree. The regexs were a quick hack. More robust parsing would definitely make sense. It's too bad that there's no portable/low-dependency way to get the C++ syntax tree and extract information from a program without some monstriosity hand-rolled thing.

@maflcko maflcko merged commit 804ca26 into bitcoin:master Jul 15, 2020
@maflcko maflcko deleted the 2006-rpcManServer branch July 15, 2020 17:45
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 16, 2020
…ommand ones (server)

fa7592b rpc: Update server to use new RPCHelpMan (MarcoFalke)
aaaaad5 rpc: Add option to hide RPCArg (MarcoFalke)
fa9708f rpc: Assert that passed arg names are equal to hardcoded ones (MarcoFalke)
faaeb2b rpc: Add CRPCCommand constructor which takes RPCHelpMan (MarcoFalke)
fa8ec00 rpc: Check that left section is not multiline (MarcoFalke)

Pull request description:

  This is split out from bitcoin#18531 to just touch the RPC methods in server. Description from the main pr:

  ### Motivation

  RPCArg names in the rpc help are currently only used for documentation. However, in the future they could be used to teach the server the named arguments. Named arguments are currently registered by the `CRPCCommand`s and duplicate the RPCArg names from the documentation. This redundancy is fragile, and has lead to errors in the past (despite having linters to catch those kind of errors). See section "bugs found" for a list of bugs that have been found as a result of the changes here.

  ### Changes

  The changes here add an assert in the `CRPCCommand` constructor that the RPCArg names are identical to the ones in the `CRPCCommand`.

  ### Future work

  > Here or follow up, makes sense to also assert type of returned UniValue?

  Sure, but let's not get ahead of ourselves. I am going to submit any further works as follow-ups, including:

  * Removing the CRPCCommand arguments, now that they are asserted to be equal and thus redundant
  * Removing all python regex linters on the args, now that RPCMan can be used to generate any output, including the cli.cpp table
  * Auto-formatting and sanity checking the RPCExamples with RPCMan
  * Checking passed-in json in self-check. Removing redundant checks
  * Checking returned json against documentation to avoid regressions or false documentation
  * Compile the RPC documentation at compile-time to ensure it doesn't change at runtime and is completely static

  ### Bugs found

  * The assert identified issue bitcoin#18607
  * The changes itself fixed bug bitcoin#19250

ACKs for top commit:
  laanwj:
    ACK fa7592b
  ryanofsky:
    Code review ACK fa7592b. Looks great! Just some hidden arg and Check() and comment cleanups since last review

Tree-SHA512: e64b6a212f4a3aeedeee47557559bde104d5fd40cdc1746b27eb2f3d4c8885d5e6e4dd287595ea11cdbc6a939654fe103cae765fd505875444d851f0abb11308
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Aug 16, 2020
…ommand ones (server)

fa7592b rpc: Update server to use new RPCHelpMan (MarcoFalke)
aaaaad5 rpc: Add option to hide RPCArg (MarcoFalke)
fa9708f rpc: Assert that passed arg names are equal to hardcoded ones (MarcoFalke)
faaeb2b rpc: Add CRPCCommand constructor which takes RPCHelpMan (MarcoFalke)
fa8ec00 rpc: Check that left section is not multiline (MarcoFalke)

Pull request description:

  This is split out from bitcoin#18531 to just touch the RPC methods in server. Description from the main pr:

  ### Motivation

  RPCArg names in the rpc help are currently only used for documentation. However, in the future they could be used to teach the server the named arguments. Named arguments are currently registered by the `CRPCCommand`s and duplicate the RPCArg names from the documentation. This redundancy is fragile, and has lead to errors in the past (despite having linters to catch those kind of errors). See section "bugs found" for a list of bugs that have been found as a result of the changes here.

  ### Changes

  The changes here add an assert in the `CRPCCommand` constructor that the RPCArg names are identical to the ones in the `CRPCCommand`.

  ### Future work

  > Here or follow up, makes sense to also assert type of returned UniValue?

  Sure, but let's not get ahead of ourselves. I am going to submit any further works as follow-ups, including:

  * Removing the CRPCCommand arguments, now that they are asserted to be equal and thus redundant
  * Removing all python regex linters on the args, now that RPCMan can be used to generate any output, including the cli.cpp table
  * Auto-formatting and sanity checking the RPCExamples with RPCMan
  * Checking passed-in json in self-check. Removing redundant checks
  * Checking returned json against documentation to avoid regressions or false documentation
  * Compile the RPC documentation at compile-time to ensure it doesn't change at runtime and is completely static

  ### Bugs found

  * The assert identified issue bitcoin#18607
  * The changes itself fixed bug bitcoin#19250

ACKs for top commit:
  laanwj:
    ACK fa7592b
  ryanofsky:
    Code review ACK fa7592b. Looks great! Just some hidden arg and Check() and comment cleanups since last review

Tree-SHA512: e64b6a212f4a3aeedeee47557559bde104d5fd40cdc1746b27eb2f3d4c8885d5e6e4dd287595ea11cdbc6a939654fe103cae765fd505875444d851f0abb11308
@maflcko maflcko mentioned this pull request Aug 4, 2021
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Sep 1, 2021
Summary:
PR description:
> Motivation
>
> RPCArg names in the rpc help are currently only used for documentation. However, in the future they could be used to teach the server the named arguments. Named arguments are currently registered by the CRPCCommands and duplicate the RPCArg names from the documentation. This redundancy is fragile, and has lead to errors in the past (despite having linters to catch those kind of errors).
>
> Changes
>
> The changes here add an assert in the CRPCCommand constructor that the RPCArg names are identical to the ones in the CRPCCommand.

This is a backport of [[bitcoin/bitcoin#19386 | core#19386]] [1/5]
bitcoin/bitcoin@fa8ec00

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D10006
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Sep 10, 2021
Summary:
This allows the constructor to ask the rpc manager for the name of the
rpc method or the rpc argument names instead of having it manually
passed in.

Note: added underscores to some constructors' arguments to prevent shadowing fields, like previously done in D5906

This is a backport of [[bitcoin/bitcoin#19386 | core#19386]] [2/5]
bitcoin/bitcoin@faaeb2b

Depends on D10006

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D10007
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Sep 10, 2021
Summary:
This is a backport of [[bitcoin/bitcoin#19386 | core#19386]] [3/5]
bitcoin/bitcoin@fa9708f

Depends on D10007

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D10008
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Sep 10, 2021
Summary:
Also, update switch statments to our style guide

This is a backport of [[bitcoin/bitcoin#19386 | core#19386]] [4/5]
bitcoin/bitcoin@aaaaad5

Depends on D10008

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D10009
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Sep 10, 2021
Summary:
Also, move Check to inside HandleRequest

This is a backport of [[bitcoin/bitcoin#19386 | core#19386]]
bitcoin/bitcoin@fa7592b

Depends on D10009

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta, Fabien

Reviewed By: #bitcoin_abc, majcosta, Fabien

Subscribers: Fabien, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D10010
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants