rest: Use query parameters to control resource loading #24098

pull stickies-v wants to merge 6 commits into bitcoin:master from stickies-v:rest/use-query-parameters changing 11 files +333 −95
  1. stickies-v commented at 9:06 pm on January 18, 2022: contributor

    In RESTful APIs, typically path parameters (e.g. /some/unique/resource/) are used to represent resources, and query parameters (e.g. ?sort=asc) are used to control how these resources are being loaded through e.g. sorting, pagination, filtering, …

    As first [discussed in #17631](https://github.com/bitcoin/bitcoin/pull/17631#discussion_r733031180), the current REST api contains two endpoints /headers/ and /blockfilterheaders/ that rather unexpectedly use path parameters to control how many (filter) headers are returned in the response. While this is no critical issue, it is unintuitive and we are still early enough to easily phase this behaviour out and ensure new endpoints (if any) do not have to stick to non-standard behaviour just for internal consistency.

    In this PR, a new HTTPRequest::GetQueryParameter method is introduced to easily parse query parameters, as well as two new /headers/ and /blockfilterheaders/ endpoints that use a count query parameter are introduced. The old path parameter-based endpoints are kept without too much overhead, but the documentation now points to the new query parameter-based endpoints as the default interface to encourage standardness.

    Behaviour change

    New endpoints and default values

    /headers/ and /blockfilterheaders/ now have 2 new endpoints that contain query parameters (?count=<count>) instead of path parameters (/<count>/), as described in REST-interface.md. Since query parameters can easily have default values, I have set this at 5 for both endpoints.

    headers GET /rest/headers/<BLOCK-HASH>.<bin|hex|json>?count=<COUNT=5> should now be used instead of GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json>

    blockfilterheaders GET /rest/blockfilterheaders/<FILTERTYPE>/<BLOCK-HASH>.<bin|hex|json>?count=<COUNT=5> should now be used instead of GET /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH>.<bin|hex|json>

    Some previously invalid API calls are now valid

    API calls that contained query strings in the URI could not be parsed prior to this PR. This PR changes behaviour in that previously invalid calls (e.g. GET /rest/headers/5/somehash.json?someunusedparam=foo) would now become valid, as the query parameters are properly parsed, and discarded if unused. For example, prior to this PR, adding an irrelevant someparam parameter would be illegal:

    0GET /rest/headers/5/0000004c6aad0c89c1c060e8e116dcd849e0554935cd78ff9c6a398abeac6eda.json?someparam=true
    1->
    2Invalid hash: 0000004c6aad0c89c1c060e8e116dcd849e0554935cd78ff9c6a398abeac6eda.json?someparam=true
    

    This behaviour change affects all rest endpoints, not just the 2 new ones introduced here.

    (Note: I’d be open to implementing additional logic to refuse requests containing unrecognized query parameters to minimize behaviour change, but for the endpoints that we currently have I don’t really see the point for that added complexity. E.g. I don’t see any scenarios where misspelling a parameter could lead to harmful outcomes)

    Using the REST API

    To run the API HTTP server, start a bitcoind instance with the -rest flag enabled. To use the blockfilterheaders endpoint, you’ll also need to set -blockfilterindex=1:

    0./bitcoind -signet -rest -blockfilterindex=1
    

    As soon as bitcoind is fully up and running, you should be able to query the API, for example by using curl on the command line: curl "127.0.0.1:38332/rest/chaininfo.json". To more easily parse the JSON output, you can also use tools like ‘jq’ or json_pp, e.g.:

    0curl -s "localhost:38332/rest/blockfilterheaders/basic/0000004c6aad0c89c1c060e8e116dcd849e0554935cd78ff9c6a398abeac6eda.json?count=2" | json_pp .
    

    To do

    • update doc/release-notes

    Feedback

    This is my first PR (hooray!). Please don’t hold back on any feedback/comments/nits/… you may have, big or small, whether they are code, process, language, … related. I welcome private messages too if there’s anything you don’t want to clutter the PR with. I’m here to learn and am grateful for everyone’s input.

  2. stickies-v force-pushed on Jan 18, 2022
  3. DrahtBot added the label Build system on Jan 18, 2022
  4. DrahtBot added the label RPC/REST/ZMQ on Jan 18, 2022
  5. DrahtBot commented at 5:48 am on January 19, 2022: contributor

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #24456 (blockman: Properly guard blockfile members by dongcarl)
    • #23599 (Tidy up RPCTxSerializationFlags by MarcoFalke)
    • #23507 (Refactor: Improve API design of ScriptToUniv, TxToUniv etc to return the UniValue instead of mutating a parameter/reference by mjdietzx)
    • #22953 (refactor: introduce single-separator split helper (boost::split replacement) by theStack)
    • #22564 (refactor: Move mutable globals cleared in ::UnloadBlockIndex to BlockManager by dongcarl)
    • #21422 (Add feerate histogram to getmempoolinfo by kiminuo)

    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.

  6. DrahtBot added the label Needs rebase on Jan 19, 2022
  7. stickies-v force-pushed on Jan 19, 2022
  8. stickies-v commented at 10:29 am on January 19, 2022: contributor
    Force pushed to rebase after #24054 was merged; no other changes.
  9. DrahtBot removed the label Needs rebase on Jan 19, 2022
  10. brunoerg commented at 11:19 am on January 19, 2022: contributor

    Concept ACK

    Tested ?count=1, ?count=5 and ?count=600 (didn’t paste here the result of it, obviously haha) for /rest/headers/

    0http://127.0.0.1:8332/rest/headers/000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a.json?count=1
    1
    2[{"hash":"000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a","confirmations":237003,"height":50000,"version":1,"versionHex":"00000001","merkleroot":"27f1d66f8a1ee5280f4e92508dcb647e954d53004905d08a75574daee4988360","time":1270916538,"mediantime":1270915355,"nonce":58489010,"bits":"1c2a1115","difficulty":6.085476906000794,"chainwork":"00000000000000000000000000000000000000000000000000014a426593e15b","nTx":1,"previousblockhash":"000000000845517b31c6820d83f25cff46429bf136a7515fe504116427e60f8e","nextblockhash":"000000001c920d495e1eeef2452b6d1c6c229a919b28196c103ecffebabee141"}]
    3
    4http://127.0.0.1:8332/rest/headers/000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a.json?count=5
    5
    6[{"hash":"000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a","confirmations":240714,"height":50000,"version":1,"versionHex":"00000001","merkleroot":"27f1d66f8a1ee5280f4e92508dcb647e954d53004905d08a75574daee4988360","time":1270916538,"mediantime":1270915355,"nonce":58489010,"bits":"1c2a1115","difficulty":6.085476906000794,"chainwork":"00000000000000000000000000000000000000000000000000014a426593e15b","nTx":1,"previousblockhash":"000000000845517b31c6820d83f25cff46429bf136a7515fe504116427e60f8e","nextblockhash":"000000001c920d495e1eeef2452b6d1c6c229a919b28196c103ecffebabee141"},{"hash":"000000001c920d495e1eeef2452b6d1c6c229a919b28196c103ecffebabee141","confirmations":240713,"height":50001,"version":1,"versionHex":"00000001","merkleroot":"ee3a2d2b895cafacff526d06a55b55e049cf84a9735e4a63f7fd08f96d0f4649","time":1270917100,"mediantime":1270915522,"nonce":56717043,"bits":"1c2a1115","difficulty":6.085476906000794,"chainwork":"00000000000000000000000000000000000000000000000000014a487b7bc7c6","nTx":3,"previousblockhash":"000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a","nextblockhash":"000000002066d7f9b134c30b5005b7bf5fbfa52f279883f1fde793dcdc964266"},{"hash":"000000002066d7f9b134c30b5005b7bf5fbfa52f279883f1fde793dcdc964266","confirmations":240712,"height":50002,"version":1,"versionHex":"00000001","merkleroot":"5164dc2785549f9efe14eb1c54522ec1874a02b7eda164fde370c05412f037ad","time":1270917181,"mediantime":1270915874,"nonce":9804838,"bits":"1c2a1115","difficulty":6.085476906000794,"chainwork":"00000000000000000000000000000000000000000000000000014a4e9163ae31","nTx":1,"previousblockhash":"000000001c920d495e1eeef2452b6d1c6c229a919b28196c103ecffebabee141","nextblockhash":"00000000237ce1b692f5ef9f1f9ac64d62d5e6a24dcfec08633fa2d339434e2a"},{"hash":"00000000237ce1b692f5ef9f1f9ac64d62d5e6a24dcfec08633fa2d339434e2a","confirmations":240711,"height":50003,"version":1,"versionHex":"00000001","merkleroot":"792c4a613e51966fc1c852cd8d94fd10a05efa55fffdd6a41281c8d139540b23","time":1270917916,"mediantime":1270916181,"nonce":311792473,"bits":"1c2a1115","difficulty":6.085476906000794,"chainwork":"00000000000000000000000000000000000000000000000000014a54a74b949c","nTx":1,"previousblockhash":"000000002066d7f9b134c30b5005b7bf5fbfa52f279883f1fde793dcdc964266","nextblockhash":"000000000528e8aac90e3096abb7b1f3d52efa2cd8f12754614788cb43eefe11"},{"hash":"000000000528e8aac90e3096abb7b1f3d52efa2cd8f12754614788cb43eefe11","confirmations":240710,"height":50004,"version":1,"versionHex":"00000001","merkleroot":"3cfbd9a91f033e37f05764215626c542c735ed71cd5b53467834cf881c0b9ebd","time":1270918264,"mediantime":1270916354,"nonce":78553962,"bits":"1c2a1115","difficulty":6.085476906000794,"chainwork":"00000000000000000000000000000000000000000000000000014a5abd337b07","nTx":1,"previousblockhash":"00000000237ce1b692f5ef9f1f9ac64d62d5e6a24dcfec08633fa2d339434e2a","nextblockhash":"0000000000e1ad737f11fe4cb3d126408b2cbcc28ab0d83293f408e413733338"}]
    

    I tested the same values but using /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json> format and it worked as well. I didn’t understand Deprecated (but not removed) since 23.0:, will both ways be kept?

  11. MarcoFalke commented at 11:30 am on January 19, 2022: member
    Is there a large cost to maintaining both ways? If not, it might be better to keep both to not upset users for a weak motivation.
  12. in src/rest.cpp:218 in f861b6df05 outdated
    219     }
    220 
    221-    std::string hashStr = path[1];
    222     uint256 hash;
    223     if (!ParseHashStr(hashStr, hash))
    224         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
    


    brunoerg commented at 11:31 am on January 19, 2022:

    if I try to pass a blockhash without the extension (.<bin|hex|json>) in /headers, like: /rest/headers/000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a?count=5 returns: Invalid hash: 000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a?count=5

    In the deprecated, /rest/headers/5/000000001aeae195809d120b5d66a39c83eb48792e068f8ea1fea19d84a4278a returns: output format not found (available: .bin, .hex, .json)


    stickies-v commented at 1:36 pm on January 19, 2022:

    You’re right, that should be handled better. I’ve:

    • updated ParseDataFormat to ignore the query string even when no data format is found
    • updated documentation on ParseDataFormat in rest.h to document existing and new behaviour
    • added a test case to test_query_string to capture what you’ve found

    Rebased and pushed to 0415aaa


    brunoerg commented at 6:10 pm on January 22, 2022:
    Tested! Works!
  13. stickies-v force-pushed on Jan 19, 2022
  14. stickies-v commented at 1:47 pm on January 19, 2022: contributor

    Is there a large cost to maintaining both ways? If not, it might be better to keep both to not upset users for a weak motivation.

    The PR is already setup to maintain both ways, so anyone using the (to be) deprecated endpoints should not be affected. This is documented in REST-interface.md too, so users are aware it is still supported but that they should probably use the new endpoint for new applications. The maintenance overhead is minimal, basically in rest_headers and rest_filter_header we have an if/else branch that adds 3 LoC to maintain the old endpoints too. Some additional refactoring could be done to simplify without the two branches, but it’s no big deal.

    Personally, I would definitely not remove the old endpoints in the next release, and probably not until some bigger API refactoring would make this desirable, in which case we should notify users more explicitly. I did not add any deprecation logging to avoid behaviour change, and because I think we don’t have to push users away from the old endpoints yet.

  15. stickies-v commented at 1:53 pm on January 19, 2022: contributor

    I didn’t understand Deprecated (but not removed) since 23.0:, will both ways be kept?

    Thank you for the review! Correct, I argue both should be kept at least for now, and that’s how it’s currently implemented. I’m open to suggestions for better phrasing, but typically deprecated means a feature is no longer developed and may be removed in the future, but is still available. See e.g. Microsoft’s definition. Does that clear things up?

  16. jsarenik commented at 4:46 pm on January 19, 2022: none
    @stickies-v instead of deprecating the old behavior the new can be just called backwards-compatible. By the way, an example of an externalized backward-compatible REST-service (in a way similar to what you have here) using Caddy webserver can be seen at https://github.com/jsarenik/bitcoin-faucet-shell/blob/master/Caddyfile.txt
  17. jsarenik approved
  18. jsarenik commented at 4:49 pm on January 19, 2022: none
    utACK 0415aaafb2f7206c117f58cc7a4a967cee0fa619
  19. stickies-v commented at 10:30 am on January 20, 2022: contributor

    @stickies-v instead of deprecating the old behavior the new can be just called backwards-compatible.

    We can, but I’m not convinced we should? I think deprecating the old endpoints has a clear benefit of keeping the documented interface simpler and better adhering to common REST API practices. Simultaneously, since they’re not removed, existing users are not affected, just gently nudged towards the new interface if and when it suits them. Thank you for your review!

  20. in doc/REST-interface.md:50 in 0415aaafb2 outdated
    46@@ -47,18 +47,24 @@ The HTTP request and response are both handled entirely in-memory.
    47 With the /notxdetails/ option JSON response will only contain the transaction hash instead of the complete transaction details. The option only affects the JSON response.
    48 
    49 #### Blockheaders
    50-`GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json>`
    51+`GET /rest/headers/<BLOCK-HASH>.<bin|hex|json>?count=<COUNT=5>`
    


    promag commented at 10:00 pm on January 23, 2022:

    Why only count as a query parameter? How about

    0GET /rest/headers?block_hash=&count=&format=
    1
    2block_hash defaults to tip
    3count defaults to 5
    4format defaults to request accept header, JSON if not specified
    

    stickies-v commented at 1:58 pm on January 24, 2022:

    I agree and considered that, but decided to omit this for now to keep this PR’s scope limited. Would you be okay with this being incorporated in a follow up PR (which I’m happy to take on right away?)

    Edit: after reading jnewbery’s comment, I realized I didn’t see you also converted block_hash into a query parameter, which I don’t agree with as per his arguments. I still agree with your suggestion for format.

    Edit 2: after reading your latest comment, I realize I did not fully think this through. I have some further thoughts but will hold off on sharing these until after the PR review club to encourage open discussion.


    stickies-v commented at 5:15 pm on February 27, 2022:

    To conclude, I agree with promag that /headers/ and /blockfilterheaders/ are collection endpoints since we’re not obtaining a resource identified by the specified block_hash but rather use it as a pagination parameter. I didn’t properly realise this at first.

    Given that 1) this PR has had quite a bit of review time already which I’d rather not overhaul too much and 2) these and the other updates (moving format and notxdetails to a query parameter) make sense to implement together, I’ll implement this in a follow up PR (I’ve already briefly started but it will take a bit more time as I’m thinking about ways to minimise the maintenance burden of keeping backwards compatibility).


    fjahr commented at 11:25 pm on March 19, 2022:

    FWIW, I agree with @promag that this would be more properly following REST best practices, which seems to be the main point of this PR.

    Do I understand correctly that you want to keep maintaining the old structure, this structure and then also future structures like suggested above @stickies-v ? That does seem more laborious than necessary to me. You could merge these first parts of changes as intermediary steps and then add follow-ups but I would not “officially” support it by adding it to docs and release notes yet. You could make the follow-up changes and only afterwards add documentation and release notes when everything is final, so that there are at least only two versions to maintain and not one per follow-up PR since without docs and release notes it can be reasonably assumed that nobody is building a client on top of the intermediary version.

    Alternatively if you really want to include docs I would suggest in this intermediate step instead of marking the old structure as deprecated, add the new structure marked as experimental and add a comment that it is still object to change.


    stickies-v commented at 5:31 pm on April 5, 2022:
    I think your approach makes a lot of sense, fjahr. However, since there has already been significant review time spent on this and this PR seems ready for merge, I don’t think this is big enough of a problem to ask for another round of review. Even if it’s not a huge update, it still would touch quite a few commits (dropping release notes, modifying REST interface docs, updating error return messages and inline docs). I’ll definitely consider the approach you suggest for further PRs because I think it is a more careful approach, but for now I’d prefer to leave things as they are and move forward.
  21. in src/httpserver.cpp:673 in 0415aaafb2 outdated
    668+            }
    669+        }
    670+        evhttp_clear_headers(&params_q);
    671+    }
    672+
    673+    if (default_val != "")
    


    promag commented at 10:03 pm on January 23, 2022:
    Maybe use std::optional because an empty string could be the default?

    stickies-v commented at 1:56 pm on January 24, 2022:
    Agreed and done, for both GetQueryParameterFromUri and HTTPRequest:GetQueryParameter.
  22. in src/httpserver.cpp:653 in 0415aaafb2 outdated
    648+    return result;
    649+}
    650+
    651+std::string GetQueryParameterFromUri(const std::string& uri, const std::string& key, const std::string& default_val)
    652+{
    653+    auto query_start = uri.rfind('?');
    


    promag commented at 10:21 pm on January 23, 2022:
    Should use evhttp_uri_parse.

    stickies-v commented at 4:26 pm on January 24, 2022:
    Agreed and done.
  23. promag commented at 10:29 pm on January 23, 2022: member
    I don’t see a strong reason for this change TBH.
  24. stickies-v force-pushed on Jan 24, 2022
  25. stickies-v commented at 4:27 pm on January 24, 2022: contributor

    Rebased and:

    Thank you @promag for your review. To address your comment:

    I don’t see a strong reason for this change TBH

    I agree this is not a critical issue. However, I think everyone can agree my proposal is the more standard way of organizing API endpoints. If that’s true (and so far no one argued the opposite), it’s better to refactor this earlier rather than later, because for example in #17631 we (I think rightfully) preferred internal consistency over adhering to best practices. We now also have the functionality in place for other existing or new endpoints to easily accept query parameters, including your proposal for the data format.

    Note: I’ve just learned I should only rebase in case of merge conflicts. My apologies for the added review difficulty, won’t happen going forward.

  26. in src/rest.cpp:17 in e1822e79b3 outdated
    13@@ -14,6 +14,7 @@
    14 #include <node/context.h>
    15 #include <primitives/block.h>
    16 #include <primitives/transaction.h>
    17+#include <rest.h>
    


    jnewbery commented at 10:05 am on January 26, 2022:

    nit: most of the .cpp translation units include their own header file first, before all other includes, i.e.:

    0#include <rest.h>
    1
    2#include <blockfilter.h>
    3#include <chain.h>
    4#include <chainparams.h>
    5...
    

    stickies-v commented at 1:34 pm on January 26, 2022:
    Done
  27. in src/rest.h:10 in e1822e79b3 outdated
     5+#ifndef BITCOIN_REST_H
     6+#define BITCOIN_REST_H
     7+
     8+#include <string>
     9+
    10+enum class RetFormat {
    


    jnewbery commented at 10:35 am on January 26, 2022:
    Now that this enum is exposed in the header, consider using a slightly more descriptive name such as RESTRequestFormat or similar.

    stickies-v commented at 9:36 pm on January 27, 2022:
    That’s a good idea. I’ve scripted-diff renamed RetFormat to RESTResponseFormat, since this enum is currently only used to describe response format.
  28. in src/rest.cpp:144 in e1822e79b3 outdated
    142+    const std::string::size_type pos_querystr = strReq.rfind('?');
    143+    const std::string::size_type pos_format = strReq.rfind('.');
    144+    param = strReq.substr(0, std::min(pos_format, pos_querystr));
    145+
    146+    // No format string is found
    147+    if (pos_format == std::string::npos)
    


    jnewbery commented at 10:37 am on January 26, 2022:

    While touching this line:

    0    if (pos_format == std::string::npos) {
    

    stickies-v commented at 1:35 pm on January 26, 2022:
    Done

    theStack commented at 9:24 am on February 24, 2022:
    This doesn’t seem to be resolved?

    stickies-v commented at 11:59 am on February 25, 2022:
    Sorry, I think I refactored it out again after simplifying ParseDataFormat a bit more. Didn’t realize our coding style preferred brackets for single-statement multi-line if statements. Added brackets again, thanks for catching this.
  29. in src/test/rest_tests.cpp:33 in e1822e79b3 outdated
    28+    // Query string with multiple parameters
    29+    rf = ParseDataFormat(param, "/rest/endpoint/someresource.json?p1=v1&p2=v2");
    30+    BOOST_CHECK_EQUAL(param, "/rest/endpoint/someresource");
    31+    BOOST_CHECK_EQUAL(rf, RetFormat::JSON);
    32+
    33+    // Incorrectly formed query string will not be handled
    


    jnewbery commented at 11:51 am on January 26, 2022:
    Perhaps also test a URI that contains a ? before ., eg "/rest/endpoint/someresource?p1=v1.json")

    stickies-v commented at 0:52 am on January 28, 2022:
    Added an additional test case. More importantly, the handling of this edge case led me to simplify ParseDataFormat by refactoring out the pos_querystr variable.
  30. in src/rest.cpp:377 in e1822e79b3 outdated
    374+    if (uri_parts.size() == 3) {
    375+        // deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>
    376+        blockhash_index = 2;
    377+        raw_count = uri_parts[1];
    378+    }
    379+    else if (uri_parts.size() == 2) {
    


    jnewbery commented at 11:58 am on January 26, 2022:
    0    } else if (uri_parts.size() == 2) {
    

    stickies-v commented at 1:36 pm on January 26, 2022:
    Done
  31. in src/rest.cpp:382 in e1822e79b3 outdated
    379+    else if (uri_parts.size() == 2) {
    380+        // new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
    381+        blockhash_index = 1;
    382+        raw_count = req->GetQueryParameter("count", "5");
    383+    }
    384+    else {
    


    jnewbery commented at 11:58 am on January 26, 2022:
    0    } else {
    

    stickies-v commented at 1:37 pm on January 26, 2022:
    Done
  32. in src/httpserver.h:96 in e1822e79b3 outdated
    91+     * would instead parse this as an array of values, but this is not (yet) implemented as it is
    92+     * currently not needed in any of the endpoints.
    93+     *
    94+     * @param[in] key represents the query parameter of which the value is returned
    95+     * @param[in] default_val (optional) is returned if key is not in query string
    96+     * @throws std::runtime_error if key is not in query string and no default_val was provided
    


    jnewbery commented at 12:06 pm on January 26, 2022:

    We don’t usually use exceptions to return error conditions in the Bitcoin Core codebase. If you do this, wherever there’s a key that doesn’t have a default, the calling code will have to wrap this call in a try/except, and then catch the runtime_error and return it as a RESTERR.

    Did you consider returning a std::optional<std::string>, and then using std::optional::value_or() to add the default, and std::optional::has_value() to catch if a non-default option hasn’t been set?


    stickies-v commented at 10:38 pm on January 27, 2022:
    I did not consider that, but that’s a great idea. I’ve taken it a bit further, and removed the default_val optional argument from GetQueryParameter, since this functionality can now just as easily be achieved in the headers endpoints by using std::optional::value_or() as you suggested, slightly reducing complexity of the code.
  33. in src/rest.cpp:386 in e1822e79b3 outdated
    383+    }
    384+    else {
    385+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
    386+    }
    387+
    388+    auto parsed_count = ToIntegral<size_t>(raw_count);
    


    jnewbery commented at 12:10 pm on January 26, 2022:
    Any reason to lose the const here?

    stickies-v commented at 10:34 pm on January 27, 2022:
    Good catch, this is leftover from before I introduced the new raw_count variable but indeed should be const now. Fixed in both rest_headers and rest_filter_header.
  34. in test/functional/interface_rest.py:56 in e1822e79b3 outdated
    57-            rest_uri += '.json'
    58-        elif req_type == ReqType.BIN:
    59-            rest_uri += '.bin'
    60-        elif req_type == ReqType.HEX:
    61-            rest_uri += '.hex'
    62+        query_string = ''
    


    jnewbery commented at 12:11 pm on January 26, 2022:
    Did you consider making query_string an optional argument to test_rest_request()? It seems a bit contrived to pass the URI without the req_type as part of the string, but with the query_string.

    stickies-v commented at 2:16 am on January 28, 2022:
    I agree, that’s a more elegant solution. To facilitate dynamic URI creation, I’ve added a query_params kwargs to test_rest_request() which is then encoded into a query string.
  35. jnewbery commented at 12:18 pm on January 26, 2022: member

    Concept ACK. A few minor comments inline.

    Why only count as a query parameter? How about

    GET /rest/headers?block_hash=&count=&format=

    block_hash defaults to tip count defaults to 5 format defaults to request accept header, JSON if not specified

    Each /headers/block is a unique resource, so I think it makes sense to keep the block hash as part of the URI path. Making format a query string key seems ok to me.

    I also think that the /rest/block/notxdetails/ endpoint could be deprecated and merged into /rest/block/<BLOCKHASH>?txdetails=true|false or similar in a follow up.

  36. stickies-v force-pushed on Jan 28, 2022
  37. stickies-v force-pushed on Jan 28, 2022
  38. stickies-v commented at 3:02 am on January 28, 2022: contributor

    Thank you for the extensive review jnewbery, I’ve incorporated all of your comments into the latest force push 9d9f177.

    Each /headers/block is a unique resource, so I think it makes sense to keep the block hash as part of the URI path. Making format a query string key seems ok to me.

    I also think that the /rest/block/notxdetails/ endpoint could be deprecated and merged into /rest/block/<BLOCKHASH>?txdetails=true|false or similar in a follow up.

    I agree with both of your comments, and am happy to take them on in a follow up PR. Since all of the query parameter logic is already in place now, this should be quite limited in scope too.

  39. DrahtBot added the label Needs rebase on Feb 2, 2022
  40. promag commented at 9:46 am on February 2, 2022: member

    Each /headers/block is a unique resource, so I think it makes sense to keep the block hash as part of the URI path. @jnewbery this endpoint returns a list of count headers, not the unique resource.

    0// list of headers
    1GET /rest/headers?block_hash=&count=&format=
    2
    3VS
    4
    5// an header
    6GET /rest/headers/block_hash?format=
    

    Anyway, just wanted to note my opinion, I’m fine with block hash in the path.

  41. stickies-v force-pushed on Feb 2, 2022
  42. stickies-v commented at 11:02 am on February 2, 2022: contributor
    Rebased to fix merge conflict from #24223.
  43. DrahtBot removed the label Needs rebase on Feb 2, 2022
  44. stickies-v force-pushed on Feb 2, 2022
  45. LarryRuane commented at 3:43 pm on February 23, 2022: contributor

    It may be helpful to other reviews to note that you can interact with a local bitcoind’s REST interface from the command line using curl, for example:

    0$ curl -s localhost:8332/rest/blockhashbyheight/723418.json | jq .
    1{
    2  "blockhash": "000000000000000000010ce22c300e67d706860e8e883ed108691a903801b0ee"
    3}
    

    Another common JSON formatting tool is json_pp (if you don’t have jq installed).

  46. theStack commented at 4:55 pm on February 23, 2022: contributor

    Concept ACK

    Makes sense to follow best practices of REST API design.

  47. OliverOffing commented at 5:57 pm on February 23, 2022: none
    concept ACK
  48. in src/rest.cpp:149 in 833803e9aa outdated
    153+    if (pos_format == std::string::npos)
    154+        return rf_names[0].rf;
    155 
    156+    // Match format string to available formats
    157+    auto len_suff = param.size() - pos_format - 1;  // - 1 so we don't include the leading .
    158+    const std::string suff(param, pos_format + 1, len_suff);
    


    theStack commented at 9:01 am on February 24, 2022:

    Calculating the length shouldn’t be necessary here, you can simply omit the third parameter of the ctor to get the sub-string from pos_format + 1 to the end:

    0    const std::string suff(param, pos_format + 1);
    

    (I checked that tests still pass after this change) Also, I’d suggest to rename “suff” to “suffix” to make it more clear (nit though)


    stickies-v commented at 4:35 pm on February 24, 2022:
    Ah yes, you’re right, that is more elegant. Updated to your suggestion, and also renamed suff to suffix.
  49. in src/rest.h:24 in 833803e9aa outdated
    19+ * and query string.
    20+ *
    21+ * @param[out]  param       The strReq without the data format string and
    22+ *                          without the query string (if any).
    23+ * @param[in]   strReq      The URI to be parsed.
    24+ * @return      RESTResponseFormat   RESTResponseFormat that was parsed from the URI.
    


    theStack commented at 9:07 am on February 24, 2022:

    nit: no reason to explicitly menton the return type here before the description (we don’t do that usually as far as I saw)

    0 * [@return](/bitcoin-bitcoin/contributor/return/)      RESTResponseFormat that was parsed from the URI.
    

    stickies-v commented at 4:42 pm on February 24, 2022:
    Okay didn’t know, thanks, and you’re right, by far most places in this codebase don’t include the return type. Fixed
  50. in src/test/rest_tests.cpp:26 in 833803e9aa outdated
    21+    BOOST_CHECK_EQUAL(rf, RESTResponseFormat::JSON);
    22+
    23+    // Query string with single parameter
    24+    rf = ParseDataFormat(param, "/rest/endpoint/someresource.json?p1=v1");
    25+    BOOST_CHECK_EQUAL(param, "/rest/endpoint/someresource");
    26+    BOOST_CHECK_EQUAL(rf, RESTResponseFormat::JSON);
    


    theStack commented at 9:16 am on February 24, 2022:
    Could use .bin here (and .hex below, or vice versa) to also test all the different response formats.

    stickies-v commented at 4:49 pm on February 24, 2022:
    Like the idea, incorporated!

    stickies-v commented at 4:50 pm on February 24, 2022:
    Like the idea, incorporated!
  51. in doc/release-notes-24098.md:20 in 6628e8f3b0 outdated
    15+  `GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json>` (deprecated)
    16+
    17+  For `/blockfilterheaders/`, use
    18+  `GET /rest/blockfilterheaders/<FILTERTYPE>/<BLOCK-HASH>.<bin|hex|json>?count=<COUNT=5>`
    19+  instead of
    20+  `GET /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH>.<bin|hex|json>`
    


    theStack commented at 9:21 am on February 24, 2022:

    nit: to be consistent with the headers description above:

    0  `GET /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH>.<bin|hex|json> (deprecated)`
    

    stickies-v commented at 4:27 pm on February 24, 2022:
    Thanks, fixed
  52. glozow commented at 1:23 pm on February 25, 2022: member
    Concept ACK, I don’t know much about REST but am convinced that query parameter instead of path makes more sense for specifying count.
  53. theStack commented at 1:50 pm on February 25, 2022: contributor
    @stickies-v: Seems like you didn’t force-push your latest changes yet?
  54. w0xlt commented at 3:00 pm on February 25, 2022: contributor

    Approach ACK. I agree that a query parameter is more adequate for the count parameter. Also agree on the refactoring and the test added.

    I think the commit 833803e can be split in two: one for the change mentioned in the title of commit and other for the test added.

  55. stickies-v force-pushed on Feb 27, 2022
  56. stickies-v commented at 7:39 pm on February 27, 2022: contributor

    Force pushed changes to incorporate suggestions from theStack and an re-unresolved comment from jnewbery. As per my comment in the thread, I think the best approach is to move the block_hash query parameter into the follow up PR along with some more improvements, so I think for now all comments on the content of this PR are resolved.

    It may be helpful to other reviews to note that you can interact with a local bitcoind’s REST interface from the command line using curl, for example:

    Thanks for the suggestion, I updated the initial post with some instructions.

    @stickies-v: Seems like you didn’t force-push your latest changes yet?

    My bad, I tended to use the “Mark as resolved” as a personal to do list which I now realize is confusing, going forward I’ll try to force push first and comment/mark afterwards.

    I think the commit 833803e can be split in two: one for the change mentioned in the title of commit and other for the test added.

    Could you please explain how that would improve the PR in your view? I think since the test is quite small, so closely linked with the code changes, and wouldn’t work without the prior commit, it’s quite intuitive to keep it in one? I had a look at other PRs and I couldn’t really find consensus between doing tests in the same or separate commits, so I just went for my intuition. (edit: and to add to that, it would probably complicate the re-review process as git range-diff would break

  57. in src/httpserver.cpp:649 in b5d8761251 outdated
    640@@ -639,6 +641,39 @@ HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const
    641     }
    642 }
    643 
    644+std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string& key) const
    645+{
    646+    auto uri = evhttp_request_get_uri(req);
    647+    auto result = GetQueryParameterFromUri(uri, key);
    648+
    649+    return result;
    


    theStack commented at 11:22 pm on February 28, 2022:

    nit: could just directly return the result of the helper instead of storing it in a variable:

    0    return GetQueryParameterFromUri(uri, key);
    
  58. in src/httpserver.cpp:654 in b5d8761251 outdated
    649+    return result;
    650+}
    651+
    652+std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key)
    653+{
    654+    auto uri_parsed = evhttp_uri_parse(uri);
    


    theStack commented at 11:29 pm on February 28, 2022:
    nit: Personally I’m not a fan of using the auto keyword for results of external C library calls (or any other calls that return pointers to objects, especially if they need further calls to clean up). It’s less to type, but overall harder to reason about the code, IMHO.

    stickies-v commented at 12:23 pm on March 1, 2022:
    I hadn’t considered this but I agree - updated this and in HTTPRequest::GetQueryParameter.
  59. in src/httpserver.cpp:656 in b5d8761251 outdated
    651+
    652+std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key)
    653+{
    654+    auto uri_parsed = evhttp_uri_parse(uri);
    655+    auto query = evhttp_uri_get_query(uri_parsed);
    656+    std::optional<std::string> result {};
    


    theStack commented at 11:41 pm on February 28, 2022:

    nit: As far as I know we usually don’t explicitly specify the empty ctor (there are also instances of those in other commits).

    0    std::optional<std::string> result;
    

    stickies-v commented at 12:23 pm on March 1, 2022:
    Thanks, I wasn’t aware of this convention. I’ve updated this and 3 other zero-inits of std::string. I’m still zero-init’ing size_t blockhash_index {}; as to my understanding long doesn’t have a default ctor and this could be unsafe otherwise.
  60. in src/rest.cpp:208 in e87b32e8ea outdated
    207+    } else if (path.size() == 1) {
    208+        // new path with query parameter: /rest/headers/<hash>?count=<count>
    209+        hashStr = path[0];
    210+        raw_count = req->GetQueryParameter("count").value_or("5");
    211+    } else {
    212+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid request. Use /rest/headers/<hash>.<ext>?count=<count>");
    


    theStack commented at 11:50 pm on February 28, 2022:

    nit: For consistency reasons, could adapt to the corresponding error message format for blockfilterheaders:

    0        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
    
  61. theStack commented at 0:04 am on March 1, 2022: contributor
    Left some more review comments, mostly code-style nit-picking though, feel free to ignore. Changes generally LGTM and I think the PR is pretty close to a mergeable state.
  62. stickies-v force-pushed on Mar 1, 2022
  63. stickies-v commented at 12:30 pm on March 1, 2022: contributor

    Left some more review comments, mostly code-style nit-picking though, feel free to ignore. Changes generally LGTM and I think the PR is pretty close to a mergeable state.

    Thank you for the thorough review, I’ve incorporated all of your suggestions. Hoping to get some final ACKs to get this over the line, I agree that it feels like being close to merged 👍

  64. in src/httpserver.cpp:668 in 25cd29e6bd outdated
    664+            if (param->key == key) {
    665+                result = param->value;
    666+                break;
    667+            }
    668+        }
    669+        evhttp_clear_headers(&params_q);
    


    jnewbery commented at 1:35 pm on March 1, 2022:
    Are you sure this evhttp_clear_headers() call is necessary? The evkeyvalq struct is destructed immediately after this.

    stickies-v commented at 4:47 pm on March 2, 2022:

    As we discussed offline, it looks like it is necessary since evkeyvalq is a linked list so we need to make sure that all elements are deleted with evhttp_clear_headers().

    To my understanding, this is not necessary in e.g. HTTPRequest::GetHeader() because we obtain a pointer to evkeyvalq* headers where the underlying object is managed by the evhttp_request* req, which in turn should properly destroy its associated headers at the end of its lifetime (and not before that). In this case, evkeyvalq params_q is managed by GetQueryParameterFromUri directly. Does that make sense or am I missing something?


    jnewbery commented at 10:13 am on March 9, 2022:
    Yes, that looks correct to me. Thanks for looking into this!
  65. in src/httpserver.cpp:659 in 25cd29e6bd outdated
    654+    const char* query = evhttp_uri_get_query(uri_parsed);
    655+    std::optional<std::string> result;
    656+
    657+    if (query) {
    658+        // Parse the query string into a key-value queue and iterate over it
    659+        struct evkeyvalq params_q;
    


    jnewbery commented at 1:35 pm on March 1, 2022:

    Is the struct keyword needed here?

    0       evkeyvalq params_q;
    

    stickies-v commented at 3:17 pm on March 1, 2022:
    This confused me too. I don’t see how it’s necessary to keep it, but that’s how libevent documents it and also how it’s already used everywhere (1, 2, 3) in Bitcoin Core, as well as in most (all?) unofficial examples/documentation you can find online. I thought it might be a good idea to keep it to stay in line with libevent conventions. Knowing that, would you still prefer to have it removed? I don’t have a strong view either way, but think I prefer sticking with the convention.

    jnewbery commented at 10:25 am on March 9, 2022:

    ok, I’ve dug into this a bit more, and it’s an elaborated type specifier. It means that even if we have a local variable named evkeyvalq, then this would still compile, since the struct class-key would mean the local variable is ignored.

    I think it’s fine to leave it.

  66. in src/httpserver.cpp:663 in 25cd29e6bd outdated
    658+        // Parse the query string into a key-value queue and iterate over it
    659+        struct evkeyvalq params_q;
    660+        evhttp_parse_query_str(query, &params_q);
    661+
    662+        struct evkeyval* param;
    663+        for (param = params_q.tqh_first; param; param = param->next.tqe_next) {
    


    jnewbery commented at 1:39 pm on March 1, 2022:

    I’d suggest a couple of changes:

    • put the param declaration in the for loop init-statement
    • explicitly compare param with nullptr:
    0        for (evkeyval* param{params_q.tqh_first}; param != nullptr; param = param->next.tqe_next) {
    

    stickies-v commented at 4:51 pm on March 2, 2022:
    Done - although as per my other comment I’ve kept the struct prefix for now to stick with convention.
  67. in src/rest.cpp:368 in 25cd29e6bd outdated
    366     std::vector<std::string> uri_parts;
    367     boost::split(uri_parts, param, boost::is_any_of("/"));
    368-    if (uri_parts.size() != 3) {
    369-        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>");
    370+    std::string raw_count;
    371+    size_t blockhash_index {};
    


    jnewbery commented at 1:46 pm on March 1, 2022:

    What do you think about handling the blockhash in the same way as the count:

     0--- a/src/rest.cpp
     1+++ b/src/rest.cpp
     2@@ -365,14 +365,14 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
     3     std::vector<std::string> uri_parts;
     4     boost::split(uri_parts, param, boost::is_any_of("/"));
     5     std::string raw_count;
     6-    size_t blockhash_index {};
     7+    std::string raw_blockhash;
     8     if (uri_parts.size() == 3) {
     9         // deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>
    10-        blockhash_index = 2;
    11+        raw_blockhash = uri_parts[2];
    12         raw_count = uri_parts[1];
    13     } else if (uri_parts.size() == 2) {
    14         // new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
    15-        blockhash_index = 1;
    16+        raw_blockhash = uri_parts[1];
    17         raw_count = req->GetQueryParameter("count").value_or("5");
    18     } else {
    19         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
    20@@ -384,8 +384,8 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
    21     }
    22 
    23     uint256 block_hash;
    24-    if (!ParseHashStr(uri_parts[blockhash_index], block_hash)) {
    25-        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[blockhash_index]);
    26+    if (!ParseHashStr(raw_blockhash, block_hash)) {
    27+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash);
    28     }
    29 
    30     BlockFilterType filtertype;
    
  68. stickies-v force-pushed on Mar 2, 2022
  69. theStack approved
  70. theStack commented at 10:28 am on March 3, 2022: contributor
    ACK fe95d30ae4ffd1946823ec3e2d6a59c063d62893 🌱
  71. in src/httpserver.cpp:662 in fe95d30ae4 outdated
    657+    if (query) {
    658+        // Parse the query string into a key-value queue and iterate over it
    659+        struct evkeyvalq params_q;
    660+        evhttp_parse_query_str(query, &params_q);
    661+
    662+        for (struct evkeyval* param { params_q.tqh_first }; param != nullptr; param = param->next.tqe_next) {
    


    jnewbery commented at 10:30 am on March 9, 2022:

    Sorry to nitpick, but the normal style is to not put spaces around braces when using braced initialization:

    0        for (struct evkeyval* param{params_q.tqh_first}; param != nullptr; param = param->next.tqe_next) {
    

    stickies-v commented at 11:32 am on March 10, 2022:
    Fixed, wasn’t aware of this convention, thanks. I really do appreciate the nits, consistent style is important.
  72. in src/rest.cpp:136 in fe95d30ae4 outdated
    132@@ -137,25 +133,28 @@ static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
    133     return node_context->chainman.get();
    134 }
    135 
    136-static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
    137+RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq)
    


    jnewbery commented at 12:00 pm on March 9, 2022:

    You remove the static keyword in commit 2c84d3916b7 (Handle query string when parsing data format), but it should be removed in the earlier commit 1cc759af21 (Refactoring: move declarations to rest.h) at the same time that you add the ParseDataFormat declaration to rest.h. Without removing that keyword, the commit fails to compile with:

    0rest.cpp:136:18: error: RetFormat ParseDataFormat(std::string&, const string&) was declared extern and later static [-fpermissive]
    1  136 | static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
    2      |                  ^~~~~~~~~~~~~~~
    3In file included from rest.cpp:6:
    4./rest.h:17:11: note: previous declaration of RetFormat ParseDataFormat(std::string&, const string&)
    5   17 | RetFormat ParseDataFormat(std::string& param, const std::string& strReq);
    6      |           ^~~~~~~~~~~~~~~
    

    stickies-v commented at 11:34 am on March 10, 2022:

    Yikes, this shouldn’t have happened, thanks for catching that. I’ve adapted this script to ensure all commits in the branch build:

     0BRANCH=$(git rev-parse --abbrev-ref HEAD)
     1COMMITS=$(git log master..$BRANCH --oneline  --reverse | cut -d " " -f 1)
     2
     3for COMMIT in $COMMITS
     4do
     5  git -c advice.detachedHead=false checkout $COMMIT > /dev/null
     6  make -j "$(($(sysctl -n hw.physicalcpu)+1))" > /dev/null  # on linux, use: make -j "$(($(nproc)+1))" > /dev/null
     7  if [ $? -eq 0 ]
     8  then
     9        echo $COMMIT - passed
    10    else
    11        echo $COMMIT - failed
    12        git checkout $BRANCH
    13        exit
    14    fi
    15done
    16echo all commits on $BRANCH built successfully
    17
    18git checkout $BRANCH
    

    MarcoFalke commented at 11:47 am on March 10, 2022:
  73. in src/rest.cpp:211 in fe95d30ae4 outdated
    211+        raw_count = req->GetQueryParameter("count").value_or("5");
    212+    } else {
    213+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
    214+    }
    215+
    216+    const auto parsed_count = ToIntegral<size_t>(raw_count);
    


    jnewbery commented at 5:24 pm on March 9, 2022:
    Is there a reason you’ve changed parsed_count from using braced initialization to using = initialization?

    stickies-v commented at 11:35 am on March 10, 2022:
    I don’t think this was an intentional change. I’ve changed both parsed_count definitions to use brace initialization. I’ve also updated all other new/modified = initializations I could find to use brace initialization. Specifically, this is in ParseDataFormat, HTTPRequest::GetQueryParameter and GetQueryParameterFromUri
  74. in src/rest.cpp:381 in fe95d30ae4 outdated
    379+        raw_count = req->GetQueryParameter("count").value_or("5");
    380+    } else {
    381+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
    382+    }
    383+
    384+    const auto parsed_count = ToIntegral<size_t>(raw_count);
    


    jnewbery commented at 5:25 pm on March 9, 2022:
    Same question about changing from braced initialization to = initialization.

    stickies-v commented at 11:35 am on March 10, 2022:
    Fixed
  75. jnewbery commented at 5:37 pm on March 9, 2022: member

    nearly ACK fe95d30ae4ffd1946823ec3e2d6a59c063d62893

    I’ve left a few minor comments. The only one that I’d definitely like to see resolved before merge is the build failure on the first two commits.

  76. Refactoring: move declarations to rest.h
    This facilitates unit testing
    9f1c54787c
  77. scripted-diff: rename RetFormat to RESTResponseFormat
    As RetFormat is now exposed in a header, it is renamed to the more
    understandable RESTResponseFormat
    -BEGIN VERIFY SCRIPT-
    s() { sed -i 's/RetFormat/RESTResponseFormat/g' $1; }
    s src/rest.cpp
    s src/rest.h
    -END VERIFY SCRIPT-
    c1aad1b3b9
  78. Handle query string when parsing data format
    URLs may contain a query string (prefixed with '?') and this should be ignored when parsing
    the data format.
    
    To facilitate testing this functionality, ParseDataFormat has been made non-static.
    fff771ee86
  79. Add GetQueryParameter helper function
    Easily get the query parameter from the URI, with optional default value.
    a09497614e
  80. stickies-v force-pushed on Mar 10, 2022
  81. stickies-v commented at 11:41 am on March 10, 2022: contributor

    Force pushed from fe95d30 to 9aac438 to address jnewbery’s comments, namely not each commit being able to compile and several style updates regarding spaces and brace initialization. There are no functional/behaviour changes.

    The range-diff (git range-diff 219d728 fe95d30 9aac438) is a bit noisy unfortunately, you can also compare the commits individually:

    0git diff 1cc759af212c91807735e6c9bc2c998468c7252a..9f1c54787c81177dd56a31c881a9ad2834a122dc
    1git diff 40830ae7ea72c6ac6082bbfd30d26d1f749c46df..c1aad1b3b95b7c6bdf05e0c2095aba2f2db8310b
    2git diff 2c84d3916b710fbf6029858acff941549e875cb7..fff771ee864975cee8c831651239bac95503c37a
    3git diff ac3c007a393d3493ad9023c71d4ed3c38a8baabd..a09497614e9bb603fff36286d9611a25b23eeb02
    4git diff f610294c449aefc4bdae9175292fedbe5dd1d5f5..46af138c216d5fa7ac512a42c8219706e97d88fa
    5git diff fe95d30ae4ffd1946823ec3e2d6a59c063d62893..9aac4381897130ce630c4c3f5fca6690cd148330
    
  82. jnewbery commented at 10:15 am on March 30, 2022: member

    Code review ACK 9aac4381897130ce630c4c3f5fca6690cd148330

    Tested range-diff and that all commits build.

  83. theStack approved
  84. theStack commented at 3:19 pm on March 30, 2022: contributor
    re-ACK 9aac4381897130ce630c4c3f5fca6690cd148330
  85. Update /<count>/ endpoints to use a '?count=' query parameter instead
    In most RESTful APIs, path parameters are used to represent resources, and
    query parameters are used to control how these resources are being filtered/sorted/...
    
    The old /<count>/ functionality is kept alive to maintain backwards compatibility,
    but new paths with query parameters are introduced and documented as the default
    interface so future API methods don't break consistency by using query parameters.
    f959fc0397
  86. Add release notes 54b39cfb34
  87. stickies-v force-pushed on Apr 5, 2022
  88. stickies-v commented at 5:33 pm on April 5, 2022: contributor

    I’ve had to push a tiny doc update to REST-interface.md (git range-diff 219d728 9aac438 54b39cf) since this was not merged for v23, but since there are no significant changes beyond theStack and jnewbery’s ACKs I think this PR is now ready to be considered for merging? @MarcoFalke

     01:  9f1c54787 = 1:  9f1c54787 Refactoring: move declarations to rest.h
     12:  c1aad1b3b = 2:  c1aad1b3b scripted-diff: rename RetFormat to RESTResponseFormat
     23:  fff771ee8 = 3:  fff771ee8 Handle query string when parsing data format
     34:  a09497614 = 4:  a09497614 Add GetQueryParameter helper function
     45:  46af138c2 ! 5:  f959fc039 Update /<count>/ endpoints to use a '?count=' query parameter instead
     5    @@ doc/REST-interface.md: The HTTP request and response are both handled entirely i
     6      Given a block hash: returns <COUNT> amount of blockheaders in upward direction.
     7      Returns empty if the block doesn't exist or it isn't in the active chain.
     8      
     9    -+*Deprecated (but not removed) since 23.0:*
    10    ++*Deprecated (but not removed) since 24.0:*
    11     +`GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json>`
    12     +
    13      #### Blockfilter Headers
    14    @@ doc/REST-interface.md: The HTTP request and response are both handled entirely i
    15      direction for the filter type <FILTERTYPE>.
    16      Returns empty if the block doesn't exist or it isn't in the active chain.
    17      
    18    -+*Deprecated (but not removed) since 23.0:*
    19    ++*Deprecated (but not removed) since 24.0:*
    20     +`GET /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH>.<bin|hex|json>`
    21     +
    22      #### Blockfilters
    236:  9aac43818 = 6:  54b39cfb3 Add release notes
    
  89. MarcoFalke requested review from theStack on Apr 5, 2022
  90. MarcoFalke requested review from jnewbery on Apr 5, 2022
  91. theStack approved
  92. theStack commented at 6:40 pm on April 5, 2022: contributor

    re-ACK 54b39cfb342d10a448d49299c715e3a25c2aca4a

    Verified that the only change since my last ACK was bumping the version number in the REST documentation from v23 to v24 (since this PR didn’t make it into v23, v24 will be the first release where the old endpoints are deprecated).

  93. jnewbery commented at 8:19 pm on April 5, 2022: member
    ACK 54b39cfb342d10a448d49299c715e3a25c2aca4a
  94. MarcoFalke merged this on Apr 6, 2022
  95. MarcoFalke closed this on Apr 6, 2022

  96. sidhujag referenced this in commit 5e9ff53acf on Apr 6, 2022
  97. stickies-v deleted the branch on Apr 6, 2022
  98. bitcoin locked this on Jul 31, 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-11-23 12:12 UTC

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