cli: Parse and allow hash value #19949

pull fjahr wants to merge 2 commits into bitcoin:master from fjahr:jsonerr changing 2 files +10 −4
  1. fjahr commented at 11:15 pm on September 12, 2020: contributor

    This is an alternative approach to #15448 and also tries to eliminate “Error parsing JSON” errors. This came up in connection with getblockstats in this comment again: #19885 (comment) and that lead me to look into it.

    Conceptually I am not completely against the approach of #15448 but it stalled and maybe this approach is favored by reviewers.

  2. fjahr force-pushed on Sep 12, 2020
  3. fjahr commented at 11:38 pm on September 12, 2020: contributor
    Removed usage of isxdigit because the linter complained about locale dependency.
  4. DrahtBot added the label RPC/REST/ZMQ on Sep 13, 2020
  5. in src/rpc/client.cpp:253 in 3f64b5617a outdated
    220+
    221+static bool IsHashVal(const std::string& str) {
    222+    return (str.length() == 64) && std::all_of(str.begin(), str.end(), IsHexDigit);
    223+}
    224+
    225 /** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
    


    n-thumann commented at 1:41 pm on September 13, 2020:

    Maybe add that this method also accepts tx and blockhashes?

    0/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null or tx and blockhashes)
    

    fjahr commented at 5:39 pm on September 13, 2020:
    makes sense, I amended the comment.

    n-thumann commented at 9:31 pm on September 13, 2020:
    Oh, I forgot: You probably want to change this in src/rpc/client.h as well for consistency :)

    fjahr commented at 12:06 pm on September 14, 2020:
    Done
  6. in src/rpc/client.cpp:259 in 3f64b5617a outdated
    226  * as well as objects and arrays.
    227  */
    228 UniValue ParseNonRFCJSONValue(const std::string& strVal)
    229 {
    230     UniValue jVal;
    231     if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
    


    n-thumann commented at 1:51 pm on September 13, 2020:
    0    if ((!jVal.read(std::string("[")+strVal+std::string("]")) &&
    1        !jVal.read(std::string("[\"")+strVal+std::string("\"]"))) ||
    

    Just as an idea, feel free to ignore :) This would be shorter, requires no additional methods and also does the job (except for getblockstats "'<blockhash>'"). But - and that could be more important - it keeps the error message if the blockhash is e.g. too long (hash_or_height must be of length 64 (not 66, for ...), while your solution throws an JSON parsing error which could be misleading for a user 🤔


    fjahr commented at 5:31 pm on September 13, 2020:
    If I see that correctly what this change will do is it will end up passing everything as a string to the server and never throw that error client-side. So basically the same as #15448 which removes ParseNonRFCJSONValue and only does validation server-side (the arguably better error message is coming from the server). The feedback to that was that the validation shouldn’t be removed completely from the client-side which is the approach I am trying here.

    n-thumann commented at 9:44 pm on September 13, 2020:
    Sounds reasonable 👍
  7. fjahr force-pushed on Sep 13, 2020
  8. n-thumann commented at 9:49 pm on September 13, 2020: contributor
    tACK https://github.com/bitcoin/bitcoin/commit/5076f8363eb658d24044dab4f618c414566f4b5f: ran functional & unit tests, works as expected. Only nit are the slightly degraded error messages when entering malformed hex strings ✌️
  9. in src/rpc/client.cpp:222 in 5076f8363e outdated
    213@@ -214,15 +214,25 @@ CRPCConvertTable::CRPCConvertTable()
    214 
    215 static CRPCConvertTable rpcCvtTable;
    216 
    217+static bool IsHexDigit(const char c) {
    218+    return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
    219+}
    220+
    221+static bool IsHashVal(const std::string& str) {
    222+    return (str.length() == 64) && std::all_of(str.begin(), str.end(), IsHexDigit);
    


    promag commented at 9:30 am on September 14, 2020:
    Could use bool IsHex(const std::string&) from src/util/strencodings.h.

    fjahr commented at 10:04 am on September 14, 2020:
    Hm, my impression was that client.h/cpp should be stand-alone and not depend on other parts of the project? Maybe I am wrong about that but I thought I heard that somewhere and looking at the includes seemed to confirm it. Otherwise, I guess I could also just use ParseHashV from rpc/util.cpp?

    promag commented at 10:23 am on September 14, 2020:
    AFAIK it can’t include server side components.

    fjahr commented at 12:05 pm on September 14, 2020:
    Makes sense, changed to use IsHex.

    stickies-v commented at 2:29 pm on November 16, 2022:
    There’s also https://github.com/bitcoin/bitcoin/blob/6863ad79a65842504ab6f5584fac3d1de7ecf87e/src/core_io.h#L46 that I think you’re able to include? If you think the overhead of calling .SetHex() is too large (not sure it is), I’d be in favour of just carving it out in core_io.h instead of redefining the logic here.
  10. fjahr force-pushed on Sep 14, 2020
  11. promag commented at 8:55 am on September 21, 2020: member

    Does’t feel right to place the workaround in ParseNonRFCJSONValue.

    I prefer #15448.

  12. fjahr force-pushed on Sep 21, 2020
  13. fjahr commented at 11:07 pm on September 21, 2020: contributor

    Does’t feel right to place the workaround in ParseNonRFCJSONValue.

    I prefer #15448.

    Not sure if that changes your mind but I pulled the check out of ParseNonRFCJSONValue which seems like a simpler change anyway. @laanwj would you mind taking a quick look? This is mainly based on your feedback to #15448.

  14. DrahtBot commented at 1:11 am on September 22, 2020: contributor

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

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK laanwj, theStack
    Approach NACK stickies-v
    Stale ACK n-thumann, aureleoules

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #26506 (refactor: rpc: use convenience fn to auto parse non-string parameters by stickies-v)

    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.

  15. laanwj commented at 10:21 am on November 23, 2020: member

    I still don’t like this kind of special casing based on “how a value looks”. It adds a degree of ambiguity that can lead to nasty issues, especially if the value recognition becomes more complex. This is similar to what I said here: #15448 (comment)

    Still, I prefer this specialization to #15448, so if we really have to do something like this, mild ACK from me.

  16. ajtowns commented at 1:07 am on February 4, 2021: contributor
    Could also change getblockstats to always expect a hash rather than hash_or_height, and use #16439 to convert “@height” into the hash on the client side.
  17. laanwj commented at 9:15 pm on February 9, 2021: member

    I like the idea of doing it on the client side. That alleviates my concerns which are mostly about programmatic use.

    Although #20273 would be the most general solution, I guess this is shorter to type.

  18. theStack commented at 1:44 pm on December 20, 2021: contributor

    Concept ACK

    Are there currently any other RPCs than getblockstats that also benefit from this change? It may make sense to also update the RPC help examples of those to get rid of the quotation marks. E.g.:

    0diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
    1index 04b9c6b1c..66e6b9af5 100644
    2--- a/src/rpc/blockchain.cpp
    3+++ b/src/rpc/blockchain.cpp
    4@@ -1746,7 +1746,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) {RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},                                    }},
    5RPCExamples{
    6-                    HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
    7+                    HelpExampleCli("getblockstats", R"(00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09 '["minfeerate","avgfeerate"]')") +
    8                     HelpExampleCli("getblockstats", R"(1000 '["minfeerate","avgfeerate"]')") +                                                                                                  
    
  19. fjahr force-pushed on Dec 28, 2021
  20. fjahr commented at 8:17 pm on December 28, 2021: contributor

    Are there currently any other RPCs than getblockstats that also benefit from this change? It may make sense to also update the RPC help examples of those to get rid of the quotation marks. E.g.:

    Yepp, it’s is also useful now in gettxoutsetinfo after some changes were merged there in the meantime. I don’t know of any others. I added a commit with these changes and rebased for good measures.

  21. DrahtBot added the label Needs rebase on Jul 1, 2022
  22. fjahr force-pushed on Jul 1, 2022
  23. DrahtBot removed the label Needs rebase on Jul 1, 2022
  24. aureleoules approved
  25. aureleoules commented at 1:27 pm on October 19, 2022: member
    ACK bf12f7c3de49d226d1d282948f91fb8e47e37bb9 I think this makes the interface less confusing to use.
  26. stickies-v commented at 4:17 pm on November 16, 2022: contributor

    Approach NACK

    It’s an elegant approach, and I think we should strive for more intuitive interfaces and this PR definitely would achieve that for the cli. However I think adding these kinds of exception handling 1) increases the maintenance burden instead of fixing the problem at its source - good method/endpoint design and 2) encourage further suboptimally designed methods/endpoints because we now have the tooling to interface with them more conveniently, making it more difficult to fix in the future.

    For example, in this case I’d prefer the approach of letting the methods deal with parameter polymorphism (if they have a strong enough reason to use it in the first place), and encourage developers to be conservative with complex parameter design. I’ve updated getblockheight (but similar approach should probably work for gettxoutsetinfo` even though I didn’t look at it) in the below diff to showcase how that could work. It’s a very quick patch so please let me know if I didn’t consider some negative side effects of this approach.

     0diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
     1index f3cab7483..6bd86a5d8 100644
     2--- a/src/rpc/blockchain.cpp
     3+++ b/src/rpc/blockchain.cpp
     4@@ -109,17 +109,17 @@ static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateMan
     5     LOCK(::cs_main);
     6     CChain& active_chain = chainman.ActiveChain();
     7 
     8-    if (param.isNum()) {
     9-        const int height{param.getInt<int>()};
    10-        if (height < 0) {
    11-            throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
    12+    const auto height{ToIntegral<int>(param.get_str())};
    13+    if (height) {
    14+        if (*height < 0) {
    15+            throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", *height));
    16         }
    17         const int current_tip{active_chain.Height()};
    18-        if (height > current_tip) {
    19-            throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
    20+        if (*height > current_tip) {
    21+            throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", *height, current_tip));
    22         }
    23 
    24-        return active_chain[height];
    25+        return active_chain[*height];
    26     } else {
    27         const uint256 hash{ParseHashV(param, "hash_or_height")};
    28         const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
    29@@ -1715,7 +1715,7 @@ static RPCHelpMan getblockstats()
    30                 "\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
    31                 "It won't work for some heights with pruning.\n",
    32                 {
    33-                    {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
    34+                    {"hash_or_height", RPCArg::Type::STR, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
    35                     {"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
    36                         {
    37                             {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
    38diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
    39index 90c69db7d..be47c3b79 100644
    40--- a/src/rpc/client.cpp
    41+++ b/src/rpc/client.cpp
    42@@ -158,7 +158,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
    43     { "listdescriptors", 0, "private" },
    44     { "verifychain", 0, "checklevel" },
    45     { "verifychain", 1, "nblocks" },
    46-    { "getblockstats", 0, "hash_or_height" },
    47     { "getblockstats", 1, "stats" },
    48     { "pruneblockchain", 0, "height" },
    49     { "keypoolrefill", 0, "newsize" },
    
  27. DrahtBot added the label Needs rebase on Dec 13, 2022
  28. cli: Parse hash value 18cbb6a3f2
  29. RPC: Update hash_or_height cli examples a3aafa2d17
  30. fjahr force-pushed on Dec 19, 2022
  31. DrahtBot removed the label Needs rebase on Dec 19, 2022
  32. fjahr commented at 12:56 pm on December 20, 2022: contributor
    I am not interested enough in this anymore to argue about implementation details. Could be marked as up-for-grabs if the issue remains unsolved.
  33. fjahr closed this on Dec 20, 2022

  34. bitcoin locked this on Dec 20, 2023

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