From eb9ef04c4ef2a82d065be4c2c60d0c26ef5927fa Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Sat, 29 Sep 2018 21:43:44 +0200 Subject: [PATCH 1/3] REST: add "blockhashbyheight" call, fetch blockhash by height --- src/rest.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/rest.cpp b/src/rest.cpp index 1850c0b7a6b..3c12151c7d3 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -577,6 +577,52 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) } } +static bool rest_blockhash_by_height(HTTPRequest* req, + const std::string& str_uri_part) +{ + if (!CheckWarmup(req)) return false; + std::string height_str; + const RetFormat rf = ParseDataFormat(height_str, str_uri_part); + + int32_t blockheight; + if (!ParseInt32(height_str, &blockheight) || blockheight < 0) { + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str)); + } + + CBlockIndex* pblockindex = nullptr; + { + LOCK(cs_main); + if (blockheight > chainActive.Height()) { + return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range"); + } + pblockindex = chainActive[blockheight]; + } + switch (rf) { + case RetFormat::BINARY: { + CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION); + ss_blockhash << pblockindex->GetBlockHash(); + req->WriteHeader("Content-Type", "application/octet-stream"); + req->WriteReply(HTTP_OK, ss_blockhash.str()); + return true; + } + case RetFormat::HEX: { + req->WriteHeader("Content-Type", "text/plain"); + req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n"); + return true; + } + case RetFormat::JSON: { + req->WriteHeader("Content-Type", "application/json"); + UniValue resp = UniValue(UniValue::VOBJ); + resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex()); + req->WriteReply(HTTP_OK, resp.write() + "\n"); + return true; + } + default: { + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); + } + } +} + static const struct { const char* prefix; bool (*handler)(HTTPRequest* req, const std::string& strReq); @@ -589,6 +635,7 @@ static const struct { {"/rest/mempool/contents", rest_mempool_contents}, {"/rest/headers/", rest_headers}, {"/rest/getutxos", rest_getutxos}, + {"/rest/blockhashbyheight/", rest_blockhash_by_height}, }; void StartREST() From 579d418f74fc803b0e55ae549b5fc0bec92b796c Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Sat, 29 Sep 2018 21:44:40 +0200 Subject: [PATCH 2/3] [QA] add rest tests for /rest/blockhashbyheight/. --- test/functional/interface_rest.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index afa9de580f0..525e3e3cf0d 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -198,7 +198,7 @@ class RESTTest (BitcoinTestFramework): self.nodes[0].generate(1) # generate block to not affect upcoming tests self.sync_all() - self.log.info("Test the /block and /headers URIs") + self.log.info("Test the /block, /blockhashbyheight and /headers URIs") bb_hash = self.nodes[0].getbestblockhash() # Check binary format @@ -227,6 +227,23 @@ class RESTTest (BitcoinTestFramework): # Check json format block_json_obj = self.test_rest_request("/block/{}".format(bb_hash)) assert_equal(block_json_obj['hash'], bb_hash) + assert_equal(self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']))['blockhash'], bb_hash) + + # Check hex/bin format + resp_hex = self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']), req_type=ReqType.HEX, ret_type=RetType.OBJ) + assert_equal(resp_hex.read().decode('utf-8').rstrip(), bb_hash) + resp_bytes = self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']), req_type=ReqType.BIN, ret_type=RetType.BYTES) + blockhash = binascii.hexlify(resp_bytes[::-1]).decode('utf-8') + assert_equal(blockhash, bb_hash) + + # Check invalid blockhashbyheight requests + resp = self.test_rest_request("/blockhashbyheight/abc", ret_type=RetType.OBJ, status=400) + assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: abc") + resp = self.test_rest_request("/blockhashbyheight/1000000", ret_type=RetType.OBJ, status=404) + assert_equal(resp.read().decode('utf-8').rstrip(), "Block height out of range") + resp = self.test_rest_request("/blockhashbyheight/-1", ret_type=RetType.OBJ, status=400) + assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: -1") + self.test_rest_request("/blockhashbyheight/", ret_type=RetType.OBJ, status=400) # Compare with json block header json_obj = self.test_rest_request("/headers/1/{}".format(bb_hash)) From 42ff30ec60c6ab6a9555da57a435b09cf217ee84 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Sat, 29 Sep 2018 21:45:43 +0200 Subject: [PATCH 3/3] [Docs] add short documentation for /rest/blockhashbyheight --- doc/REST-interface.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 7010edfcd38..d33165cb823 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -31,6 +31,11 @@ With the /notxdetails/ option JSON response will only contain the transaction ha Given a block hash: returns amount of blockheaders in upward direction. +#### Blockhash by height +`GET /rest/blockhashbyheight/.` + +Given a height: returns hash of block in best-block-chain at height provided. + #### Chaininfos `GET /rest/chaininfo.json`