RPC: access RPC arguments by name #29277

pull stickies-v wants to merge 3 commits into bitcoin:master from stickies-v:2023-10/named-args-on-arg-helper changing 8 files +149 −29
  1. stickies-v commented at 8:50 pm on January 18, 2024: contributor

    Adds string overloads for the RPCHelpMan::Arg and RPCHelpMan::MaybeArg helpers to be able to access RPC arguments by name instead of index number. Especially in RPCs with a large number of parameters, this can be quite helpful.

    Example usage:

    0const auto action{self.Arg<std::string>("action")};
    

    Most of the LoC is adding test coverage and documentation updates. No behaviour change.

    An alternative approach to #27788 with significantly less overhaul.

  2. DrahtBot commented at 8:50 pm on January 18, 2024: contributor

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

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK fjahr, maflcko, ryanofsky
    Concept ACK ismaelsadeeq, tdb3

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

  3. DrahtBot added the label RPC/REST/ZMQ on Jan 18, 2024
  4. ismaelsadeeq commented at 9:13 pm on January 18, 2024: member
    Concept ACK
  5. DrahtBot added the label CI failed on Jan 18, 2024
  6. DrahtBot commented at 9:56 pm on January 18, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/20633278087

  7. stickies-v force-pushed on Jan 18, 2024
  8. stickies-v commented at 0:09 am on January 19, 2024: contributor

    Force pushed to make CI happy:

    • removed doxygen @params since it didn’t pick up the overloads (and it’s already documented in plain-text anyway)
    • added a null return type to the testing RPCHelpMan
  9. DrahtBot removed the label CI failed on Jan 19, 2024
  10. in src/rpc/util.cpp:720 in 6f83da656d outdated
    716@@ -715,6 +717,16 @@ std::vector<std::pair<std::string, bool>> RPCHelpMan::GetArgNames() const
    717     return ret;
    718 }
    719 
    720+    size_t RPCHelpMan::GetParamIndex(std::string_view key) const
    


    fjahr commented at 5:54 pm on February 18, 2024:
    Indentation seems wrong?

    stickies-v commented at 12:49 pm on February 21, 2024:
    Thanks for spotting, fixed!
  11. fjahr commented at 6:10 pm on February 18, 2024: contributor
    Concept ACK, certainly makes the RPC code more readable. @stickies-v have you considered preparing a map once when an RPC is called rather than doing the search for the index every time an arg is accessed?
  12. maflcko commented at 11:02 am on February 19, 2024: member

    @stickies-v have you considered preparing a map once when an RPC is called rather than doing the search for the index every time an arg is accessed?

    I doubt this will have any impact. It could even perform worse, and require more code? Also, maps aren’t constexpr, so if this is moved toward constexpr/consteval, it will have to be re-written again.

  13. stickies-v force-pushed on Feb 21, 2024
  14. stickies-v commented at 12:53 pm on February 21, 2024: contributor

    Force pushed to address indentation issue (and removed the unintuitive “developer mistake” comment in docstring)

    @stickies-v have you considered preparing a map once when an RPC is called rather than doing the search for the index every time an arg is accessed?

    I agree with @maflcko’s reasoning, so going to leave this as is.

  15. in src/test/rpc_tests.cpp:587 in 0260f84ce0 outdated
    580@@ -581,4 +581,58 @@ BOOST_AUTO_TEST_CASE(help_example)
    581     BOOST_CHECK_NE(HelpExampleRpcNamed("foo", {{"arg", true}}), HelpExampleRpcNamed("foo", {{"arg", "true"}}));
    582 }
    583 
    584+static void CheckRpc(const std::vector<RPCArg>& params, const UniValue& args, RPCHelpMan::RPCMethodImpl test_impl)
    585+{
    586+    auto null_result{RPCResult{RPCResult::Type::NONE, "", "None"}};
    587+    const RPCHelpMan man{"dummy", "dummy description", params, null_result, RPCExamples{""}, test_impl};
    


    maflcko commented at 1:26 pm on February 21, 2024:
    nit in 0260f84ce07a7795b57cd84dc51d7c31a1ee15bd: s/man/rpc/g?

    stickies-v commented at 1:50 pm on March 1, 2024:
    I like that better too, thanks.
  16. in src/test/rpc_tests.cpp:597 in 0260f84ce0 outdated
    592+}
    593+
    594+BOOST_AUTO_TEST_CASE(rpc_arg_helper)
    595+{
    596+    constexpr bool DEFAULT_BOOL = true;
    597+    constexpr char DEFAULT_STRING[] = "default";
    


    maflcko commented at 1:27 pm on February 21, 2024:
    0260f84ce07a7795b57cd84dc51d7c31a1ee15bd: Why char[]? Could use auto?

    stickies-v commented at 1:50 pm on March 1, 2024:
    Done
  17. in src/rpc/util.h:415 in 28a33e1f98 outdated
    420+     * There are two overloads of this function:
    421+     * - Use Arg<Type>(size_t i) to get the argument by index or its default value.
    422+     * - Use Arg<Type>(const std::string& key) to get the argument by key or its default value.
    423+     *
    424+     * @return The value of the RPC argument as type R. If the argument is not provided
    425+     *         and has no default, a falsy value is returned.
    


    maflcko commented at 1:38 pm on February 21, 2024:
    28a33e1f9891a4ea56ba843001db84c878b0e2ea: No? It does not return a falsy value?

    stickies-v commented at 12:27 pm on March 1, 2024:
    Indeed it doesn’t, fixed. Also replaced R with Type.
  18. in src/rpc/util.h:416 in 28a33e1f98 outdated
    409-     * use MaybeArg<Type>(i) to get the optional argument or a falsy value.
    410+     * Use this function when the argument is required or when it has a default value. If the
    411+     * argument is optional and may not be provided, use MaybeArg instead.
    412      *
    413-     * The Type passed to this helper must match the corresponding
    414-     * RPCArg::Type.
    


    maflcko commented at 1:40 pm on February 21, 2024:
    why remove this?

    stickies-v commented at 12:25 pm on March 1, 2024:
    I don’t think I meant to, should be here indeed. Re-added (in both places), thanks.
  19. maflcko approved
  20. maflcko commented at 1:44 pm on February 21, 2024: member

    ACK 391873694137f241dfe1f57ed9058a438e147218 🍔

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: ACK 391873694137f241dfe1f57ed9058a438e147218 🍔
    30mHHsiB/JX9sv6SH8E4hfbfpMPUipMUdvJ7diZjq+LEVBYg0u48Usa9OFR7sl0OdlDEJ3c64WImMfBoc80W4Bg==
    
  21. DrahtBot requested review from fjahr on Feb 21, 2024
  22. DrahtBot requested review from ismaelsadeeq on Feb 21, 2024
  23. fjahr commented at 4:19 pm on February 21, 2024: contributor

    I doubt this will have any impact. It could even perform worse, and require more code?

    Same amount of code and I don’t see how this would perform worse: https://github.com/fjahr/bitcoin/commit/cbda2cf2c012030544ffc03620cbfcb91b1984be I also find this easier to reason about.

    Also, maps aren’t constexpr, so if this is moved toward constexpr/consteval, it will have to be re-written again.

    I guess I don’t have enough insight into future development plans of the RPC to judge that if is. But either way it seems weird to me that the best solution should be a find_if each time any argument is accessed (which comes out to O(n^2)), especially when we are already iterating over that information once before in the RPCHelperMan constructor. So it feels to me like this could be rewritten in the future just as likely.

  24. maflcko commented at 4:42 pm on February 21, 2024: member

    Same amount of code and I don’t see how this would perform worse: fjahr@cbda2cf I also find this easier to reason about.

    You removed the CHECK_NONFATAL to mark the bug as an internal bug.

    which comes out to O(n^2)

    The O notation only makes sense for large n. Is there more than one RPC with more than n=3? Again, if you measure it, I wouldn’t be surprised if the runtime is slower for a map. Though, you likely won’t be able to find an end-to-end performance difference, regardless of what is used.

    In any case, it shouldn’t matter much, since apparently it can easily be reverted if the switch to constexpr is done.

  25. in src/rpc/blockchain.cpp:2153 in 3918736941 outdated
    2148@@ -2149,15 +2149,16 @@ static RPCHelpMan scantxoutset()
    2149         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2150 {
    2151     UniValue result(UniValue::VOBJ);
    2152-    if (request.params[0].get_str() == "status") {
    2153+    const auto action{self.Arg<std::string>("action")};
    2154+    if (action == "status") {
    


    tdb3 commented at 2:55 pm on February 29, 2024:

    nit: What is the rationale for using the new Arg() overload in some locations but not others? Increasing consistency of usage consistency may increase readability. For example, action == <string literal> replaces request.params[0].get_str() in: https://github.com/bitcoin/bitcoin/blob/391873694137f241dfe1f57ed9058a438e147218/src/rpc/blockchain.cpp#L2153 but not later in:

    https://github.com/bitcoin/bitcoin/blob/391873694137f241dfe1f57ed9058a438e147218/src/rpc/blockchain.cpp#L2239


    stickies-v commented at 12:12 pm on March 1, 2024:

    Thanks for catching, I missed that. Updated. This is the only such inconsistency I could find in the PR - did you see any others?

    note:

    • request.params[1] in scantxoutset is not yet updated because Arg doesn’t support arrays yet, which is unrelated to this PR but absolutely warrants a follow-up
    • request.params[2] in getbalance is not updated because ParseIncludeWatchonly requires a UniValue, and I don’t want to scope creep this PR too much (same for a couple other args)

    tdb3 commented at 11:51 pm on March 4, 2024:

    Throughout the files modified for the PR, looks like there might be other opportunities to use the new overloading (e.g. other instances of params[0].get_str()). Some examples are referenced below. It would seem reasonable to scope this PR for the overloading and initial usage, and batch more updates in a subsequent PR (helps keep this PR small/digestible). It’s your call as the PR author. Great PR, and I would support either approach.

    In scanblocks(): https://github.com/bitcoin/bitcoin/blob/30a6c999351041d6a1e8712a9659be1296a1b46a/src/rpc/blockchain.cpp#L2344

    In generateblock(): https://github.com/bitcoin/bitcoin/blob/30a6c999351041d6a1e8712a9659be1296a1b46a/src/rpc/mining.cpp#L326

    In addconnection(): https://github.com/bitcoin/bitcoin/blob/30a6c999351041d6a1e8712a9659be1296a1b46a/src/rpc/net.cpp#L392


    stickies-v commented at 11:20 am on March 5, 2024:
    There are many more opportunities indeed but I’ve limited it to a small number of RPCs now to limit the scope of the PR, so I’m going to keep this as is. Thanks for your review!
  26. tdb3 commented at 2:59 pm on February 29, 2024: contributor

    Nice addition. Increases readability.
    Concept ACK.

    Inline comment provided.

    Built and exercised unit tests and rpc* functional tests. All passed.

  27. rpc: add arg helper unit test
    Compare the results of self.Arg with the request.params accessors
    to ensure they behave the same way.
    13525e0c24
  28. rpc: add named arg helper
    Overload the Arg and MaybeArg helpers to allow accessing arguments
    by name as well.
    
    Also update the docs to document Arg and MaybeArg separately
    bbb31269bf
  29. rpc: access some args by name
    Use the new key-based Arg helper in a few locations to show how
    it is used.
    30a6c99935
  30. stickies-v force-pushed on Mar 1, 2024
  31. stickies-v commented at 2:15 pm on March 1, 2024: contributor

    Force-pushed to:

    • rebase to latest master to address CI failure (?)
    • fix some documentation inaccuracies (1, 2)
    • address some test nits (1, 2)
    • consistently use action variable in scantxoutset as per this comment

    Thank you all for the review, apologies for the slow follow-up while I was on holidays.

    Same amount of code and I don’t see how this would perform worse: fjahr@cbda2cf I also find this easier to reason about.

    Thank for writing out the code, I had assumed it’d be a bit more overhead (even after adding the CHECK_NONFATAL which can be done in just one line). I would still prefer to stick with the current approach. As maflcko suggests, the number of parameters is always going to be very small, so given that we don’t have to allocate a map I’m unsure how this would perform differently but I don’t think it’s worth investigating given that both will be very fast. I do prefer this approach being more self-contained inside GetParamIndex, as opposed to adding another member variable and adding to the already verbose RPCHelpMan constructor, so will keep as is.

  32. maflcko commented at 9:59 am on April 10, 2024: member
    Does it work with OBJ_NAMED_PARAMS?
  33. stickies-v commented at 3:08 pm on April 16, 2024: contributor

    Does it work with OBJ_NAMED_PARAMS?

    Nope. I think we should just address the general use case of getting OBJ/ARR args with the Arg helpers, and then that should help with OBJ_NAMED_PARAMS too?

  34. in src/rpc/mining.cpp:125 in 30a6c99935
    121@@ -122,7 +122,7 @@ static RPCHelpMan getnetworkhashps()
    122 {
    123     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    124     LOCK(cs_main);
    125-    return GetNetworkHashPS(self.Arg<int>(0), self.Arg<int>(1), chainman.ActiveChain());
    126+    return GetNetworkHashPS(self.Arg<int>("nblocks"), self.Arg<int>("height"), chainman.ActiveChain());
    


    maflcko commented at 3:11 pm on April 16, 2024:
    It could make sense to remove the index-based access and require the name-based access?

    maflcko commented at 3:36 pm on April 29, 2024:
  35. fjahr commented at 7:49 pm on April 28, 2024: contributor

    Code review ACK 30a6c999351041d6a1e8712a9659be1296a1b46a

    I still prefer my suggested approach but it’s still an improvement and I don’t want to block this.

  36. DrahtBot requested review from maflcko on Apr 28, 2024
  37. DrahtBot requested review from tdb3 on Apr 28, 2024
  38. maflcko commented at 10:38 am on April 29, 2024: member

    ACK 30a6c999351041d6a1e8712a9659be1296a1b46a 🥑

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: ACK 30a6c999351041d6a1e8712a9659be1296a1b46a 🥑
    33lgAk6/J8fpwQT5TCz4m/ixg4qM63zVPfUgyz+Lr5wGYqRByfvE0Ol9M1LF33YNs0KZW2MwO0Ic4dnbvd2Q6BQ==
    
  39. ryanofsky approved
  40. ryanofsky commented at 2:33 pm on April 29, 2024: contributor

    Code review ACK 30a6c999351041d6a1e8712a9659be1296a1b46a. Nice change! Implementation is surprisingly simple and additional unit test coverage is welcome, too.

    Does it work with OBJ_NAMED_PARAMS?

    Nope. I think we should just address the general use case of getting OBJ/ARR args with the Arg helpers, and then that should help with OBJ_NAMED_PARAMS too?

    It would be nice to support this for consistency, but benefit is probably more minor since these arguments are already accessed by name, and omitting this keeps the code very simple.

  41. ryanofsky merged this on Apr 29, 2024
  42. ryanofsky closed this on Apr 29, 2024

  43. stickies-v deleted the branch on Apr 29, 2024

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-09-28 22:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me