Expose block filters over REST #17631

pull TheBlueMatt wants to merge 2 commits into bitcoin:master from TheBlueMatt:2019-11-filter-rest changing 3 files +231 −5
  1. TheBlueMatt commented at 4:45 am on November 29, 2019: member

    This adds a new rest endpoint: /rest/blockfilter/filtertype/requesttype/blockhash (eg /rest/blockfilter/basic/header/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f.hex) which exposes either the filter “header” or the filter data itself. Most of the code is cribbed from the equivalent RPC.

    You can test it at http://bitcoin-rest.bitcoin.ninja/rest//blockfilter/basic/header/000000005b7a58a939b2636f61fa4ddd62258c5fed57667a35d23f2334c4f86d.hex

  2. TheBlueMatt force-pushed on Nov 29, 2019
  3. DrahtBot added the label RPC/REST/ZMQ on Nov 29, 2019
  4. promag commented at 9:00 am on November 29, 2019: member
    A test would be nice.
  5. practicalswift commented at 9:24 am on November 29, 2019: contributor

    After reading doc/REST-interface.md I’m not entirely clear about the assumed trust boundaries.

    What recommendations do we give to our users regarding exposing the REST endpoints publicly? Do the recommendations differ from our recommendations with regards to exposing the JSON-RPC endpoints publicly?

    As I’ve understood it we regard the JSON-RPC interface as as an internal control plane only to be accessible by trusted clients. The assumption we’re making from a trust boundary perspective seems to be that we assume that an untrusted clients will never be able to connect to the port serving the JSON-RPC interface (which is the same port as the REST interface).

  6. laanwj commented at 9:32 am on November 29, 2019: member

    Concept ACK.

    After reading doc/REST-interface.md I’m not entirely clear about the assumed trust boundaries.

    That’s a fair question (FWIW the limit has always been: only public data, no complex queries, do not parse JSON as input), but I’d suggest opening a new issue for it. Please keep this one for review of the code changes.

  7. practicalswift commented at 9:37 am on November 29, 2019: contributor
    @laanwj Without knowing if consumers are trusted or not it is pretty hard to review it from a security perspective :)
  8. laanwj commented at 9:42 am on November 29, 2019: member

    The REST interface is a lightweight interface for querying public data. Consumers are trusted but less so than on RPC (as they don’t authenticate). I still wouldn’t recommend exposing it directly to the internet. But maybe it’s OK to open it “publicly” inside some LAN or VPN that your applications run in.

    This is my last general comment on this, please open a new issue if you want to continue this discussion.

    doc/REST-interface.md

    Speaking of which, please update the documentation to mention this new call.

  9. in src/rest.cpp:305 in 661f03f035 outdated
    280+    if (uriParts.size() != 3) {
    281+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilter/filtertype/requesttype/blockhash");
    282+    }
    283+
    284+    uint256 block_hash;
    285+    if (!ParseHashStr(uriParts[2], block_hash)) {
    


    emilengler commented at 2:55 pm on November 29, 2019:
    Nit, the use of a single line if statement would be better (including the other one line statements as well)

    TheBlueMatt commented at 6:57 pm on November 29, 2019:
    Would be much too long a line to do that.

    emilengler commented at 11:30 pm on November 29, 2019:
    Oh sorry I meant to ignore the brackets…
  10. jnewbery commented at 7:21 pm on November 29, 2019: member
    Concept ACK
  11. TheBlueMatt force-pushed on Nov 29, 2019
  12. TheBlueMatt commented at 7:37 pm on November 29, 2019: member
    Added a basic sanity test, redid the way headers work to make it easy to get many of them just like the /headers/ request.
  13. in src/rest.cpp:300 in 6f2e02f92e outdated
    295+        return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uriParts[0]);
    296+    }
    297+
    298+    long count = strtol(uriParts[1].c_str(), nullptr, 10);
    299+    if (count < 1 || count > 2000) {
    300+        return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + uriParts[1]);
    


    paymog commented at 7:30 am on December 1, 2019:
    I think it would be useful if this error message provided the user with the valid range.

    TheBlueMatt commented at 0:28 am on December 2, 2019:
    Hmm, its the same limits as for headers (the code was copied from that section above). Not sure where to document this given the lack of equivalent documentation like we have in RPC where we can fail with help text. Maybe we should have some kind of much more informative general help system like we do for RPC, though its obviously out of scope here.

    laanwj commented at 5:31 pm on December 3, 2019:
    similarly, it’d make sense to define a constant for this, and use it in both places, instead of hardcoding 2000 (which I guess is just a sanity limit)
  14. in src/rest.cpp:299 in 6f2e02f92e outdated
    294+    if (!index) {
    295+        return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uriParts[0]);
    296+    }
    297+
    298+    long count = strtol(uriParts[1].c_str(), nullptr, 10);
    299+    if (count < 1 || count > 2000) {
    


    paymog commented at 7:31 am on December 1, 2019:
    should 2000 be turned into a constant?

    TheBlueMatt commented at 0:28 am on December 2, 2019:
    Ehh, its the net limit, and dunno where to put it? Not really worth it.
  15. in src/rest.cpp:372 in 6f2e02f92e outdated
    367+        req->WriteHeader("Content-Type", "application/json");
    368+        req->WriteReply(HTTP_OK, strJSON);
    369+        return true;
    370+    }
    371+    default: {
    372+        return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
    


    paymog commented at 7:35 am on December 1, 2019:
    If I’m reading the code right it looks like json is a valid output format too along with binary and hex.

    TheBlueMatt commented at 0:29 am on December 2, 2019:
    Hmm, good catch. Looks like headers is busted too, I fixed both.
  16. in src/rest.cpp:479 in 6f2e02f92e outdated
    397+    if (!BlockFilterTypeByName(uriParts[0], filtertype)) {
    398+        return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uriParts[0]);
    399+    }
    400+
    401+    BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
    402+    if (!index) {
    


    paymog commented at 7:37 am on December 1, 2019:
    the code for extracting filtertype, index and blockhash is shared between these two functions. Should this extraction code be pulled out into their own functions?

    TheBlueMatt commented at 0:25 am on December 2, 2019:
    Lets leave DRY’ing up rest.cpp for a separate commit. I played with it a bit and there isn’t an obvious solution here that doesn’t end up adding more lines, but the whole of REST probably could get DRY’d up a lot especially in the results-providing section.
  17. in src/rest.cpp:492 in 6f2e02f92e outdated
    407+    bool block_was_connected;
    408+    {
    409+        LOCK(cs_main);
    410+        block_index = LookupBlockIndex(block_hash);
    411+        if (!block_index) {
    412+            return RESTERR(req, HTTP_NOT_FOUND, uriParts[1] + " not found");
    


    paymog commented at 7:38 am on December 1, 2019:
    nit: can the error message be changed to "Block " + uriParts[1] + " not found"?

    TheBlueMatt commented at 0:30 am on December 2, 2019:
    Hmm, its coped from the block code, so to keep it the same everywhere I’ll leave it.
  18. jonasschnelli commented at 9:06 pm on December 1, 2019: contributor
    Concept ACK
  19. TheBlueMatt force-pushed on Dec 2, 2019
  20. TheBlueMatt force-pushed on Dec 3, 2019
  21. DrahtBot commented at 4:05 pm on December 5, 2019: member

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

    Conflicts

    No conflicts as of last run.

  22. DrahtBot added the label Needs rebase on Dec 9, 2019
  23. in src/rest.cpp:154 in 3ab6abcc4d outdated
    129@@ -128,8 +130,8 @@ static bool rest_headers(HTTPRequest* req,
    130         return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
    131 
    132     long count = strtol(path[0].c_str(), nullptr, 10);
    133-    if (count < 1 || count > 2000)
    134-        return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
    135+    if (count < 1 || count > MAX_HEADERS_RESULTS)
    136+        return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of acceptable range (1-2000): " + path[0]);
    


    luke-jr commented at 5:54 pm on January 3, 2020:
    Use %u here instead of 2000?
  24. luke-jr changes_requested
  25. luke-jr referenced this in commit 3edcc25287 on Jan 5, 2020
  26. TheBlueMatt commented at 5:40 pm on May 1, 2020: member
    Rebased.
  27. TheBlueMatt force-pushed on May 1, 2020
  28. TheBlueMatt force-pushed on May 1, 2020
  29. DrahtBot removed the label Needs rebase on May 1, 2020
  30. TheBlueMatt force-pushed on May 1, 2020
  31. TheBlueMatt force-pushed on May 2, 2020
  32. TheBlueMatt force-pushed on May 2, 2020
  33. in src/rest.cpp:32 in 16d8d2da59 outdated
    28@@ -27,6 +29,7 @@
    29 #include <univalue.h>
    30 
    31 static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
    32+static const unsigned int MAX_HEADERS_RESULTS = 2000;
    


    jnewbery commented at 7:09 pm on May 15, 2020:
    nit: constrexpr
  34. in src/rest.cpp:291 in 16d8d2da59 outdated
    287@@ -285,6 +288,206 @@ static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPa
    288     return rest_block(req, strURIPart, false);
    289 }
    290 
    291+static bool rest_filter_header(HTTPRequest* req, const std::string& strURIPart)
    


    jnewbery commented at 7:10 pm on May 15, 2020:
    Since this is all new code, can you adhere to style guide? if blocks with braces or on same lines, variable names in camel case, etc.
  35. jnewbery commented at 7:47 pm on May 15, 2020: member

    Couple of nits. Otherwise looks good.

    Commit order is slightly confused. It adds code in one commit then changes it in later commits. I’d suggest putting the fixes first, and then adding the new block filter code in the final commit.

  36. naumenkogs commented at 4:14 pm on May 18, 2020: member
    Code review ACK 16d8d2da598a7d234275454f9e053ed074eebf3f. Perhaps would be good to address the style suggestion above.
  37. luke-jr referenced this in commit 50a9e11685 on Jun 9, 2020
  38. luke-jr referenced this in commit 61e26c9a78 on Jun 9, 2020
  39. luke-jr referenced this in commit 611aac1e32 on Jun 9, 2020
  40. luke-jr referenced this in commit bd8df19765 on Nov 25, 2020
  41. luke-jr referenced this in commit 148f0de1f0 on Nov 25, 2020
  42. luke-jr referenced this in commit 949c93a624 on Nov 25, 2020
  43. luke-jr referenced this in commit 985f4191c6 on Nov 30, 2020
  44. luke-jr referenced this in commit fbbe3156e0 on Oct 10, 2021
  45. luke-jr referenced this in commit 198912d0d6 on Oct 10, 2021
  46. luke-jr referenced this in commit d0686bfa1a on Oct 10, 2021
  47. in src/rest.cpp:300 in 03d3b44e22 outdated
    295+    const RetFormat rf = ParseDataFormat(param, strURIPart);
    296+
    297+    std::vector<std::string> uriParts;
    298+    boost::split(uriParts, param, boost::is_any_of("/"));
    299+    if (uriParts.size() != 3) {
    300+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/filtertype/count/blockhash");
    


    meshcollider commented at 0:52 am on October 12, 2021:
    <> around parameters would be clearer

    TheBlueMatt commented at 3:16 am on October 12, 2021:
    Heh, that’s new since this PR was opened :)
  48. in src/rest.cpp:369 in 03d3b44e22 outdated
    313+    BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
    314+    if (!index) {
    315+        return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uriParts[0]);
    316+    }
    317+
    318+    long count = strtol(uriParts[1].c_str(), nullptr, 10);
    


    meshcollider commented at 1:00 am on October 12, 2021:
    strtol -> ParseInt32 (#20457)

    TheBlueMatt commented at 3:19 am on October 12, 2021:
    This isn’t done in rest_headers either, which this code was copied from. Should both change or is it supposed to be strtol?

    meshcollider commented at 9:56 am on October 12, 2021:
    I think its just another new code preference, to phase out locale-dependent functions. Since you’re touching up MAX_HEADERS_RESULTS in rest_headers anyway, you could do this there too. Up to you.

    MarcoFalke commented at 4:54 am on October 13, 2021:
    I think the real issue isn’t whether strtol or something else is used, but whether parsing errors are ignored or not. strtol makes it easy to ignore them, so it seems reasonable to use our wrappers. (Or alternatively, add the error handling)

    TheBlueMatt commented at 2:55 am on October 17, 2021:
    This is now const auto parsed_count{ToIntegral<size_t>(uri_parts[1])};, so good, I presume (though admittedly I don’t really know what that does….mumbles about C++).
  49. in src/rest.cpp:332 in 03d3b44e22 outdated
    327+        const CBlockIndex* pindex = LookupBlockIndex(block_hash);
    328+        while (pindex != nullptr && ::ChainActive().Contains(pindex)) {
    329+            headers.push_back(pindex);
    330+            if (headers.size() == (unsigned long)count)
    331+                break;
    332+            pindex = ::ChainActive().Next(pindex);
    


    meshcollider commented at 1:13 am on October 12, 2021:

    re: #21866

    0const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block_hash);
    1CChain& active_chain = chainman.ActiveChain();
    2...
    3pindex = active_chain.Next(pindex);
    
  50. meshcollider commented at 1:16 am on October 12, 2021: contributor
    Concept ACK. This needs a rebase, I’ve added a few examples of code that has since changed inline. It’d be good to address the style/commit ordering comments above when you rebase. Also please add the change to doc/REST-interface.md that Wladimir suggested above.
  51. in test/functional/interface_rest.py:285 in 16d8d2da59 outdated
    279@@ -280,6 +280,9 @@ def run_test(self):
    280         self.sync_all()
    281         json_obj = self.test_rest_request("/headers/5/{}".format(bb_hash))
    282         assert_equal(len(json_obj), 5)  # now we should have 5 header objects
    283+        json_obj = self.test_rest_request("/blockfilterheaders/basic/5/{}".format(bb_hash))
    284+        assert_equal(len(json_obj), 5)  # now we should have 5 filter header objects
    285+        self.test_rest_request("/blockfilter/basic/{}".format(bb_hash), req_type=ReqType.BIN, ret_type=RetType.OBJ)
    


    fanquake commented at 1:19 am on October 12, 2021:
    0        self.test_rest_request(f"/blockfilter/basic/{bb_hash}", req_type=ReqType.BIN, ret_type=RetType.OBJ)
    
  52. JeremyRubin commented at 3:58 am on October 12, 2021: contributor

    concept NACK: i’m mostly of the opinion we should deprecate the rest API and make it the responsibility of a proxy server.

    short of deprecating it, i don’t think we should add new data items.

  53. TheBlueMatt force-pushed on Oct 12, 2021
  54. TheBlueMatt commented at 4:09 am on October 12, 2021: member
    Ah! I’d missed the request for doc updates, sorry about that.
  55. TheBlueMatt commented at 4:10 am on October 12, 2021: member

    concept NACK: i’m mostly of the opinion we should deprecate the rest API and make it the responsibility of a proxy server.

    That’s a huge pain. The REST interface is nice precisely because you can build a proxy server for it easy - its like 10 lines of nginx.conf. JSON-RPC isn’t a nice protocol that is broadly supported, its basically a Bitcoin Core-only thing at this point.

  56. JeremyRubin commented at 4:13 am on October 12, 2021: contributor
    exposing this rest endpoint over NGINX is precisely how our rest endpoint should not be use, so that only serves to add confidence to my notion that it shouldn’t be a part of core. A “proper” proxy to the rest interface would sanitize all requests.
  57. TheBlueMatt commented at 4:16 am on October 12, 2021: member
    You can sanitize in NGINX too, its probably easier than some python script. But, seriously, that notion is absurd - “here, you have a running production daemon, run this hacked-together python script to make its API usable” is not the same as “we don’t want to vouch for the security of this thing, so make sure you pay close attention if you want to expose calls to the outside”.
  58. TheBlueMatt force-pushed on Oct 12, 2021
  59. TheBlueMatt force-pushed on Oct 12, 2021
  60. DrahtBot added the label Needs rebase on Oct 12, 2021
  61. in src/rest.cpp:35 in fbfbb41a4f outdated
    31@@ -30,6 +32,7 @@
    32 #include <univalue.h>
    33 
    34 static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
    35+static constexpr unsigned int MAX_HEADERS_RESULTS = 2000;
    


    luke-jr commented at 5:39 pm on October 12, 2021:
    Is it intentionally redefined rather than moving net_processing’s to a header?

    TheBlueMatt commented at 5:56 pm on October 12, 2021:
    Good catch, renamed it here.
  62. in src/rest.cpp:356 in fbfbb41a4f outdated
    349+    boost::split(uriParts, param, boost::is_any_of("/"));
    350+    if (uriParts.size() != 3) {
    351+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>");
    352+    }
    353+
    354+    uint256 blockHash;
    


    luke-jr commented at 5:40 pm on October 12, 2021:
    Variable names are supposed to be snake case like you had before…

    TheBlueMatt commented at 5:52 pm on October 12, 2021:
    John seems to disagree #17631 (review)

  63. in src/rest.cpp:377 in fbfbb41a4f outdated
    370+    if (count < 1 || (unsigned int)count > MAX_HEADERS_RESULTS) {
    371+        return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count out of acceptable range (1-%u): %s",  MAX_HEADERS_RESULTS, uriParts[1]));
    372+    }
    373+
    374+
    375+    const CBlockIndex* tip = nullptr;
    


    luke-jr commented at 5:44 pm on October 12, 2021:
    Better to leave it uninitialised here so failure to do so below (in a future PR) will hit warnings.

    luke-jr commented at 5:53 pm on October 12, 2021:
    Actually, this variable is unused?
  64. TheBlueMatt commented at 5:56 pm on October 12, 2021: member

    This pull request conflicts with the target branch and needs rebase.

    Lol come on @MarcoFalke…merging 5 day old lint fixes against conflicting feature PRs…..

  65. TheBlueMatt force-pushed on Oct 12, 2021
  66. TheBlueMatt force-pushed on Oct 12, 2021
  67. TheBlueMatt force-pushed on Oct 12, 2021
  68. DrahtBot removed the label Needs rebase on Oct 12, 2021
  69. TheBlueMatt force-pushed on Oct 12, 2021
  70. TheBlueMatt force-pushed on Oct 12, 2021
  71. in src/rest.cpp:411 in c248fc938d outdated
    405+                errmsg += " This error is unexpected and indicates index corruption.";
    406+            }
    407+
    408+            return RESTERR(req, HTTP_NOT_FOUND, errmsg);
    409+        }
    410+        filter_headers.push_back(filter_header);
    


    meshcollider commented at 4:09 am on October 13, 2021:
    could std::move this

    TheBlueMatt commented at 2:51 am on October 17, 2021:
    I don’t believe this does anything for the 32-byte array on stack?

    sipa commented at 2:53 am on October 17, 2021:
    It doesn’t make a difference, indeed.
  72. in src/rest.cpp:425 in c248fc938d outdated
    420+        std::string binaryHeader = ssHeader.str();
    421+        req->WriteHeader("Content-Type", "application/octet-stream");
    422+        req->WriteReply(HTTP_OK, binaryHeader);
    423+        return true;
    424+    }
    425+
    


    meshcollider commented at 4:11 am on October 13, 2021:
    nit: extraneous newline
  73. in src/rest.cpp:432 in c248fc938d outdated
    427+        CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
    428+        for (const uint256& header : filter_headers) {
    429+            ssHeader << header;
    430+        }
    431+
    432+        std::string strHex = HexStr(ssHeader) + "\n";
    


    meshcollider commented at 4:18 am on October 13, 2021:

    There are only two differences between BINARY and HEX:

    • ssHeader.str() and HexStr(ssHeader)
    • "application/octet-stream" vs "text/plain"

    It could be written a lot more concisely as something like this:

     0case RetFormat::BINARY:
     1case RetFormat::HEX: {
     2    CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
     3    for (const uint256& header : filter_headers) {
     4        ssHeader << header;
     5    }
     6
     7    std::string header_string = (rf == RetFormat::HEX) ? HexStr(ssHeader) : ssHeader.str();
     8    std::string content_type = (rf == RetFormat::HEX) ? "text/plain" : "application/octet-stream";
     9    req->WriteHeader("Content-Type", content_type);
    10    req->WriteReply(HTTP_OK, header_string);
    11    return true;
    12}
    

    TheBlueMatt commented at 2:53 am on October 17, 2021:
    Unless its pressing I’d vaguely rather keep the code the same as the rest of the code in the same file. At least personally I don’t find this more readable.
  74. meshcollider commented at 4:30 am on October 13, 2021: contributor
    utACK c248fc938d04f2c4418eba907f041099416714f7
  75. MarcoFalke commented at 4:52 am on October 13, 2021: member

    re #17631 (comment)

    Merge conflicts are certainly frustrating and I want to apologize for that. Though, the conflicting pull was not a “lint fix”, but a bug fix. Sure, there was no bug report, but I think that bugs should still be fixed once they are known. When the pull was created, I didn’t expect a conflict and it seems there was no activity here for more than a year (https://github.com/bitcoin/bitcoin/pull/17631#ref-pullrequest-1019792843). When the pull was merged, it had several ACKs and this one had none (IIRC). Also, it seems there was a review comment about the integer parsing still unsolved: #17631 (review). (A sister pull request #23227 was also merged after 4 days with 3 ACKs, even though there was a conflict, so I think this is generally fine).

  76. in src/rest.cpp:525 in 7cf4e22080 outdated
    521+        req->WriteReply(HTTP_OK, binaryResp);
    522+        return true;
    523+    }
    524+
    525+    case RetFormat::HEX: {
    526+        CDataStream ssResp(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
    


    MarcoFalke commented at 5:09 am on October 13, 2021:
    0        CDataStream ssResp(SER_NETWORK, PROTOCOL_VERSION);
    

    nit if you need to retouch. This shouldn’t have any effect, since no txs are serialized?


    MarcoFalke commented at 8:46 am on October 17, 2021:
    Looks like you didn’t remove this in the last force push?

    TheBlueMatt commented at 1:29 am on October 18, 2021:
    Oops, I’d misread your comment here, I thought you were asking me to add the flags like they are elsewhere in the file. I’m a bit confused why they were added elsewhere in the file since this PR was opened if they should be removed now?

    MarcoFalke commented at 8:23 am on October 18, 2021:
    They were added in commit 412bab22b23036962509d2655b68cccd726b6ba4 in 2016. RPCSerializationFlags only affect txs (and thus blocks), but nothing else.
  77. in src/rest.cpp:346 in 7cf4e22080 outdated
    339@@ -337,6 +340,213 @@ static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, co
    340     return rest_block(context, req, strURIPart, false);
    341 }
    342 
    343+
    344+static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart) {
    345+    if (!CheckWarmup(req))
    


    MarcoFalke commented at 5:11 am on October 13, 2021:

    nit, if you need to re-touch, according to clang-format:

    0static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
    1{
    2    if (!CheckWarmup(req)) {
    

    TheBlueMatt commented at 5:14 pm on October 13, 2021:
    Ah, I’d misread John’s comment at #17631 (review) as referring to all braces, not just if ones, my bad.
  78. MarcoFalke approved
  79. MarcoFalke commented at 5:11 am on October 13, 2021: member
    ACK
  80. luke-jr referenced this in commit 75bfb813f8 on Oct 17, 2021
  81. luke-jr referenced this in commit 61457cf145 on Oct 17, 2021
  82. luke-jr referenced this in commit 80c3404550 on Oct 17, 2021
  83. sipa commented at 2:55 am on October 17, 2021: member
    Concept ACK
  84. Expose block filters over REST.
    This adds a new rest endpoint:
    /rest/blockfilter/filtertype/requesttype/blockhash (eg
    /rest/blockfilter/basic/header/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f.hex)
    which exposes either the filter "header" or the filter data itself.
    Most of the code is cribbed from the equivalent RPC.
    ef7c8228fd
  85. Update REST docs with new accessors 2b64fa3251
  86. TheBlueMatt force-pushed on Oct 17, 2021
  87. TheBlueMatt commented at 2:57 am on October 17, 2021: member

    Addressed feedback, diff since the previous round of acks:

     0$ git diff-tree -U1 7cf4e22080 2b64fa3251
     1diff --git a/src/rest.cpp b/src/rest.cpp
     2index dd1be52f86..18ae350630 100644
     3--- a/src/rest.cpp
     4+++ b/src/rest.cpp
     5@@ -343,3 +343,4 @@ static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, co
     6 
     7-static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart) {
     8+static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
     9+{
    10     if (!CheckWarmup(req))
    11@@ -414,3 +415,3 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
    12     case RetFormat::BINARY: {
    13-        CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
    14+        CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
    15         for (const uint256& header : filter_headers) {
    16@@ -424,5 +425,4 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
    17     }
    18-
    19     case RetFormat::HEX: {
    20-        CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
    21+        CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
    22         for (const uint256& header : filter_headers) {
    23@@ -451,6 +451,6 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
    24     }
    25-
    26 }
    27 
    28-static bool rest_block_filter(const std::any& context, HTTPRequest* req, const std::string& strURIPart) {
    29+static bool rest_block_filter(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
    30+{
    31     if (!CheckWarmup(req))
    32@@ -523,3 +523,2 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
    33     }
    34-
    35     case RetFormat::HEX: {
    36@@ -533,3 +532,2 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
    37     }
    38-
    39     case RetFormat::JSON: {
    40@@ -542,3 +540,2 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
    41     }
    42-
    43     default: {
    44$
    
  88. dergoegge commented at 6:48 pm on October 17, 2021: member

    ACK 2b64fa3251ac5ff4b4d174f1f0be7226490dce87 - Adding blockfilters to the REST interface is analogous to serving other public data such as transactions or blocks.

    Would be nice to have the code DRY’d up at some point but i would consider it fine for now as that is true for many parts of rest.cpp .

    Consider updating the PR description with the updated endpoint paths. ie: /rest/blockfilterheaders/basic/<COUNT>/... instead of /rest/blockfilter/basic/header/....

  89. MarcoFalke renamed this:
    Expose block filters over REST.
    Expose block filters over REST
    on Oct 18, 2021
  90. in src/rest.cpp:360 in 2b64fa3251
    355+    }
    356+
    357+    uint256 block_hash;
    358+    if (!ParseHashStr(uri_parts[2], block_hash)) {
    359+        return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[2]);
    360+    }
    


    kiminuo commented at 1:29 pm on October 18, 2021:
    nit: Is this check intentionally before filtertype checks? I would expect filtertype to be checked first, unless it is done on purpose for performance reasons.
  91. in src/rest.cpp:197 in 2b64fa3251
    192@@ -190,8 +193,8 @@ static bool rest_headers(const std::any& context,
    193         return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
    194 
    195     const auto parsed_count{ToIntegral<size_t>(path[0])};
    196-    if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > 2000) {
    197-        return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
    198+    if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
    199+        return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count out of acceptable range (1-%u): %s",  MAX_REST_HEADERS_RESULTS, path[0]));
    


    kiminuo commented at 1:40 pm on October 18, 2021:
    nit: The error message may also say something like “Header count is invalid or it is out of acceptable range..” as it happens quite regularly that somebody writes “1OO” instead of “100”.
  92. in src/rest.cpp:398 in 2b64fa3251
    393+
    394+    bool index_ready = index->BlockUntilSyncedToCurrentChain();
    395+
    396+    std::vector<uint256> filter_headers;
    397+    filter_headers.reserve(*parsed_count);
    398+    for (const CBlockIndex *pindex : headers) {
    


    kiminuo commented at 1:42 pm on October 18, 2021:
    0    for (const CBlockIndex* pindex : headers) {
    
  93. in src/rest.cpp:377 in 2b64fa3251
    372+    const auto parsed_count{ToIntegral<size_t>(uri_parts[1])};
    373+    if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
    374+        return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count out of acceptable range (1-%u): %s",  MAX_REST_HEADERS_RESULTS, uri_parts[1]));
    375+    }
    376+
    377+    std::vector<const CBlockIndex *> headers;
    


    kiminuo commented at 1:42 pm on October 18, 2021:
    0    std::vector<const CBlockIndex*> headers;
    

    practicalswift commented at 7:33 am on October 19, 2021:
    Any reason not to simply do the usual clang-format -i src/rest.cpp? :)

    kiminuo commented at 7:56 am on October 19, 2021:
    AFAIK Some violations of clang format in rest.cpp are from previous PRs. But in general, it would be a good thing.
  94. in src/rest.cpp:374 in 2b64fa3251
    369+        return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
    370+    }
    371+
    372+    const auto parsed_count{ToIntegral<size_t>(uri_parts[1])};
    373+    if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
    374+        return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count out of acceptable range (1-%u): %s",  MAX_REST_HEADERS_RESULTS, uri_parts[1]));
    


    kiminuo commented at 1:43 pm on October 18, 2021:
    0        return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, uri_parts[1]));
    
  95. in src/rest.cpp:344 in 2b64fa3251
    339@@ -337,6 +340,210 @@ static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, co
    340     return rest_block(context, req, strURIPart, false);
    341 }
    342 
    343+
    344+static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
    


    jnewbery commented at 4:08 pm on October 20, 2021:
    0static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
    
  96. in test/functional/interface_rest.py:283 in 2b64fa3251
    277@@ -278,11 +278,14 @@ def run_test(self):
    278         self.sync_all()
    279         json_obj = self.test_rest_request(f"/headers/5/{bb_hash}")
    280         assert_equal(len(json_obj), 5)  # now we should have 5 header objects
    281+        json_obj = self.test_rest_request(f"/blockfilterheaders/basic/5/{bb_hash}")
    282+        assert_equal(len(json_obj), 5)  # now we should have 5 filter header objects
    283+        self.test_rest_request(f"/blockfilter/basic/{bb_hash}", req_type=ReqType.BIN, ret_type=RetType.OBJ)
    


    jnewbery commented at 4:23 pm on October 20, 2021:

    Would it make sense to do some sanity testing on the returned object? For example:

    0        obj = self.test_rest_request(f"/blockfilter/basic/{bb_hash}", req_type=ReqType.BIN, ret_type=RetType.OBJ)
    1        assert_greater_than(len(obj.read()), 0)
    

    or perhaps even verify that the filter is the same as that returned by the getblockfilter RPC.

  97. in src/rest.cpp:347 in 2b64fa3251
    339@@ -337,6 +340,210 @@ static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, co
    340     return rest_block(context, req, strURIPart, false);
    341 }
    342 
    343+
    344+static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
    345+{
    346+    if (!CheckWarmup(req))
    347+        return false;
    


    jnewbery commented at 4:56 pm on October 20, 2021:

    Current style is to have the clause on the same line:

    0    if (!CheckWarmup(req)) return false;
    

    or enclosed in braces:

    0    if (!CheckWarmup(req)) {
    1        return false;
    2    }
    

    Same for if (!CheckWarmup(req)) in rest_block_filter() below.

  98. jnewbery commented at 4:58 pm on October 20, 2021: member

    Nearly ACK. A few minor comments inline. The commit log for the first commit is outdated:

    0    Expose block filters over REST.
    1    
    2    This adds a new rest endpoint:
    3    /rest/blockfilter/filtertype/requesttype/blockhash (eg
    4    /rest/blockfilter/basic/header/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f.hex)
    5    which exposes either the filter "header" or the filter data itself.
    6    Most of the code is cribbed from the equivalent RPC.
    

    The endpoints are now /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH> and /rest/blockfilter/<FILTERTYPE>/<BLOCK-HASH>

  99. in doc/REST-interface.md:56 in 2b64fa3251
    51@@ -52,6 +52,20 @@ With the /notxdetails/ option JSON response will only contain the transaction ha
    52 Given a block hash: returns <COUNT> amount of blockheaders in upward direction.
    53 Returns empty if the block doesn't exist or it isn't in the active chain.
    54 
    55+#### Blockfilter Headers
    56+`GET /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH>.<bin|hex|json>`
    


    stickies-v commented at 6:13 pm on October 20, 2021:

    From my experience (and e.g. this stackoverflow reponse), in a RESTful API path parameters are used to represent resources, and query parameters are used to control how this resource is being filtered/sorted/… To my understanding, <COUNT> does not represent a resource but rather a control on how many blockfilterheader resources to retrieve.

    What do you think about changing the request into GET /rest/blockfilterheaders/<FILTERTYPE>/<BLOCK-HASH>.<bin|hex|json>?count=<COUNT>?


    michaelfolkson commented at 12:31 pm on December 20, 2021:
    @TheBlueMatt: Any thoughts on the above?

    dergoegge commented at 4:40 pm on December 20, 2021:
    I think the reason for this PR to use GET /rest/blockfilterheaders/<FILTERTYPE>/<COUNT>/<BLOCK-HASH><bin|hex|json> was that the blockheader endpoint already uses the same format. (GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json>) @stickies-v makes a good point but in my opinion staying consistent with the current API was the better move.

    MarcoFalke commented at 4:50 pm on December 20, 2021:
    Thanks for taking a look @dergoegge . I was thinking about this and assumed that this was done for consistency as well.

    stickies-v commented at 1:49 pm on December 21, 2021:
    I agree that consistency is important, and in that light am happy to consider this comment as resolved on this PR. However, I think having just 1 prior case should not prevent us from changing course slightly and adopting better RESTful practices. I think this can nicely be done in a separate PR, so I’m happy to look into that and see if there’s an elegant way to remain backwards compatible and still adopt/encourage best practices going forward, specifically on these /headers/ and /blockfilterheaders/ endpoints.
  100. amovfx commented at 6:16 pm on October 20, 2021: none
    I read this in the pr-review-club. I am still learning.
  101. MarcoFalke commented at 8:00 am on December 20, 2021: member

    re-ack

    This feature was roughly ready when the pull was opened more than two years ago. It’s been held back on mostly non-behaviour changing “nits”, which still haven’t been addressed yet. I don’t think they’ll be addressed any time soon, so it might be better to fix them in a follow-up at this point. Going to merge to move this forward.

  102. MarcoFalke merged this on Dec 20, 2021
  103. MarcoFalke closed this on Dec 20, 2021

  104. jnewbery commented at 8:55 am on December 20, 2021: member

    It’s been held back on mostly non-behaviour changing “nits”, which still haven’t been addressed yet. I don’t think they’ll be addressed any time soon, so it might be better to fix them in a follow-up at this point. @MarcoFalke I’m sure this wasn’t your intention, but it seems very inconsistent that for most contributors PRs will be closed if review comments haven’t been addressed, but in this case the PR was merged despite review comments not being addressed for many months. It shouldn’t be the case that certain contributors can get their PRs merged even if they don’t follow the project style guide or respond to review comments.

    I also disagree that #17631 (review) is a “nit”. It’s feedback on the interface design and changing it later would be an API break.

  105. MarcoFalke commented at 9:03 am on December 20, 2021: member

    It shouldn’t be the case that certain contributors can get their PRs merged even if they don’t follow the project style guide or respond to review comments.

    Style-only comments are not mandatory to address. This has always been the case, unless the style issue is so “severe” that a linter enforces it. If there are any style issues here that I missed and should be blocking, someone should add a linter to enforce them. Though, I couldn’t find any.

    I also disagree that #17631 (comment) is a “nit”. It’s feedback on the interface design and changing it later would be an API break.

    Apologies, my bad. I wasn’t aware there is a suggestion that requests to change the behaviour.

  106. jnewbery commented at 9:18 am on December 20, 2021: member

    Style-only comments are not mandatory to address. This has always been the case, unless the style issue is so “severe” that a linter enforces it. If there are any style issues here that I missed and should be blocking, someone should add a linter to enforce them. Though, I couldn’t find any.

    I disagree - all review comments should be addressed, even if that only means responding with a comment “I don’t intend to change this because …”. If we don’t have a culture where all review comments are responded to, then it falls to the maintainers to make the decision about whether the ignored review comments are important or not, and sometimes important review comments fall through the gaps.

  107. MarcoFalke added the label Up for grabs on Dec 20, 2021
  108. MarcoFalke added this to the milestone 23.0 on Dec 20, 2021
  109. MarcoFalke commented at 9:33 am on December 20, 2021: member
    Ok, again apologies for missing the comment(s). I’ve marked this “Up for grabs”, so that someone can address the feedback before the next major release (23.0). If no one does, I’ll probably revert the changes.
  110. JeremyRubin commented at 4:49 pm on December 20, 2021: contributor
    It also feels premature given my nack + alternative path to implementing this in #23309 not having been reviewed – were it just my NACK and no alternate, I’d agree that perhpas it was sufficiently addressed, but it was my NACK + Alternative + Acks on that approach from > 1 contributor.
  111. MarcoFalke commented at 4:55 pm on December 20, 2021: member

    I didn’t see any NACK.

    Screenshot 2021-12-20 at 17-50-57 Expose block filters over REST by TheBlueMatt · Pull Request #17631 · bitcoin bitcoin

    I have no idea how to deal with this, but maybe it could make sense to re-NACK a pull request whenever there is a change (push)? Or maybe use the “GitHub review” and click the red cross, which might not be hidden?

  112. MarcoFalke commented at 4:56 pm on December 20, 2021: member
    And to comment on the content on the NACK: I don’t see how this and #23309 are mutually exclusive.
  113. luke-jr commented at 6:00 pm on December 20, 2021: member

    I agree with Marco that this has been basically ready for years, even if it might be imperfect. It’s not too late for further improvements in followups, but blocking this on it was letting the perfect be the enemy of the good IMO.

    That being said, it does seem to be a change in merge policy (IMO for the better), and should probably be discussed with the project and applied consistently.

  114. JeremyRubin commented at 6:42 pm on December 20, 2021: contributor

    @MarcoFalke well it’s a Concept Nack, so it doesn’t get stale with changes (as opposed to an Approach Nack + Concept Ack).

    W.r.t mutually exclusive, the contention that I was holding (and @practicalswift too I think?) is that a REST API exposed for unauthenticated users without a strong statement of the security model is probably something we shouldn’t be maintaining or increasing dependency on for core, and a preference for doing it as a layer on top of a more strongly audited and maintained interface (JSON RPC) being superior. So extending the REST API and increasing dependency on it for consumers of Core is counter to the goal of removing it and replacing with ‘userland’ proxies.

  115. sipa commented at 6:52 pm on December 20, 2021: member

    @JeremyRubin I think the discussion whether to provide a wrapper, and then later whether or not to deprecate/remove the existing REST interface is mostly independent from the question of what features should be exposed by the REST interface (whether that’s provided by bitcoind directly or by a wrapper). Whatever risks exists by having a REST interface in the first place aren’t worsened by adding another method.

    I do agree the merging procedure followed here was dubious, though.

  116. sidhujag referenced this in commit dbc149e878 on Dec 20, 2021
  117. MarcoFalke commented at 9:17 am on December 21, 2021: member

    I do agree the merging procedure followed here was dubious, though.

    Ok, maybe I am being blind, but can someone explain to me what was wrong about the merging procedure?

    Did this have too little review?

    I’d say no. There were:

    If it is not possible to merge a pull request with 8 ACKs, then I honestly don’t know how much review is needed to get anything merged. I expect that 99.8% of all pull request have less than 8 ACKs.

    Was this merged before a comment should have been replied to?

    Yes, I missed the comment/question about the interface, and it should have been replied to before merge. Luckily it has been replied to after merge (and in my view resolved). The only unaddressed stuff now is refactoring nit comments.

    I don’t think the merging procedure changed here. I still think that comments should be replied to before merge. However, I also think that style comments that haven’t been replied to should not block a pull request indefinitely. If the overall direction of a pull request has received overall support (which this pull request did), then I think style-nits can be dropped or fixed in a follow-up. If the overall direction of a pull request is not agreed upon, or if the style-nits are rendering a follow-up to be a re-implementation, then I think it should not be merged (not applicable to this pull request).

    Something else?

    Again, I am not seeing anything else wrong, so please elaborate.

  118. jnewbery commented at 10:07 am on December 21, 2021: member

    can someone explain to me what was wrong about the merging procedure?

    Was this merged before a comment should have been replied to?

    Yes @MarcoFalke - this is all I’m claiming - that review comments should be acknowledged and responded to before merge. The author doesn’t have to agree with them or implement them, but if someone has taken the time to review the PR, the author should respond to that review before the PR is merged. That happens in 99% of PRs, and I don’t see why this one should have been different.

    For what it’s worth, I agree with @stickies-v that we should make the REST API RESTful. I don’t feel very strongly about it because I don’t use the REST API myself, but I think the discussion should be had before merge.

    And to be clear, I think you do a fantastic job as a maintainer. In this instance you missed something, but it should never have been your responsibility to assess whether that comment should have been ignored. It’s the author’s responsibility to address all feedback on their PR.

  119. stickies-v commented at 2:08 pm on December 21, 2021: member

    FWIW - I’ve marked my RESTful comment as resolved, since @dergoegge pointed out this is consistent with another endpoint. As such, a follow-up PR is probably the more elegant way to go. I’m also happy to incorporate the other unaddressed style change comments on #17631 into this new PR.

    I appreciate everyone’s efforts in both doing the merging and keeping the process honest. I agree that requiring an author’s explicit acknowledgement (or dismissal) of all (non-spam) comments would, in my view, be preferable.

  120. MarcoFalke removed this from the milestone 23.0 on Dec 22, 2021
  121. MarcoFalke removed the label Up for grabs on Dec 22, 2021
  122. MarcoFalke commented at 9:44 am on December 22, 2021: member
    Looks like there is a follow up in #23836, so I’ve removed the “up for grabs” label again.
  123. fanquake referenced this in commit 9d099b02d8 on Jan 2, 2022
  124. sidhujag referenced this in commit d7d5eb3753 on Jan 2, 2022
  125. stickies-v commented at 10:22 pm on January 18, 2022: member
    #24098 is now ready for review to address the last unaddressed comments (by myself) on this PR.
  126. MarcoFalke referenced this in commit 27cfaeed1e on Apr 6, 2022
  127. DrahtBot locked this on Jan 18, 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-07-03 10:13 UTC

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