diff options
author | Jonas Schnelli <dev@jonasschnelli.ch> | 2019-01-22 19:58:43 -1000 |
---|---|---|
committer | Jonas Schnelli <dev@jonasschnelli.ch> | 2019-01-22 19:59:02 -1000 |
commit | 82cf6813a4ef1b4a5439eb6cddb1ab426f3c31a2 (patch) | |
tree | ba2fdc8d1b261d777e19c496bc4059f03cb3be43 | |
parent | 94167e2b5b7b6cb3cb1465ee67dd50676c5c8a14 (diff) | |
parent | 42ff30ec60c6ab6a9555da57a435b09cf217ee84 (diff) |
Merge #14353: REST: add blockhash call, fetch blockhash by height
42ff30ec6 [Docs] add short documentation for /rest/blockhashbyheight (Jonas Schnelli)
579d418f7 [QA] add rest tests for /rest/blockhashbyheight/<HEIGHT>.<FORMAT> (Jonas Schnelli)
eb9ef04c4 REST: add "blockhashbyheight" call, fetch blockhash by height (Jonas Schnelli)
Pull request description:
Completes the REST interface for trivial block exploring by adding a call that allows to fetch the blockhash in the main chain by a given height.
Tree-SHA512: 94be9e56718f857279b11cc16dfa8d04f3b5a762e87ae54281b4d87247c71c844895f4944d5a47f09056bf851f4c4761ac4fbdbaaee957265d14de5c1c73e8d2
-rw-r--r-- | doc/REST-interface.md | 5 | ||||
-rw-r--r-- | src/rest.cpp | 47 | ||||
-rwxr-xr-x | test/functional/interface_rest.py | 19 |
3 files changed, 70 insertions, 1 deletions
diff --git a/doc/REST-interface.md b/doc/REST-interface.md index ff7ef6ce1c..d21df36130 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -39,6 +39,11 @@ With the /notxdetails/ option JSON response will only contain the transaction ha Given a block hash: returns <COUNT> amount of blockheaders in upward direction. Returns empty if the block doesn't exist or it isn't in the active chain. +#### Blockhash by height +`GET /rest/blockhashbyheight/<HEIGHT>.<bin|hex|json>` + +Given a height: returns hash of block in best-block-chain at height provided. + #### Chaininfos `GET /rest/chaininfo.json` diff --git a/src/rest.cpp b/src/rest.cpp index 4f26e3afb5..c7a627d14e 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -575,6 +575,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); @@ -587,6 +633,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() diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 23b13fc4f1..d5a1b53408 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 result if block does not exists @@ -237,6 +237,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)) |