Skip to content

Commit 82cf681

Browse files
committed
Merge #14353: REST: add blockhash call, fetch blockhash by height
42ff30e [Docs] add short documentation for /rest/blockhashbyheight (Jonas Schnelli) 579d418 [QA] add rest tests for /rest/blockhashbyheight/<HEIGHT>.<FORMAT> (Jonas Schnelli) eb9ef04 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
2 parents 94167e2 + 42ff30e commit 82cf681

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

doc/REST-interface.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ With the /notxdetails/ option JSON response will only contain the transaction ha
3939
Given a block hash: returns <COUNT> amount of blockheaders in upward direction.
4040
Returns empty if the block doesn't exist or it isn't in the active chain.
4141

42+
#### Blockhash by height
43+
`GET /rest/blockhashbyheight/<HEIGHT>.<bin|hex|json>`
44+
45+
Given a height: returns hash of block in best-block-chain at height provided.
46+
4247
#### Chaininfos
4348
`GET /rest/chaininfo.json`
4449

src/rest.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,52 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
575575
}
576576
}
577577

578+
static bool rest_blockhash_by_height(HTTPRequest* req,
579+
const std::string& str_uri_part)
580+
{
581+
if (!CheckWarmup(req)) return false;
582+
std::string height_str;
583+
const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
584+
585+
int32_t blockheight;
586+
if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
587+
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
588+
}
589+
590+
CBlockIndex* pblockindex = nullptr;
591+
{
592+
LOCK(cs_main);
593+
if (blockheight > chainActive.Height()) {
594+
return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
595+
}
596+
pblockindex = chainActive[blockheight];
597+
}
598+
switch (rf) {
599+
case RetFormat::BINARY: {
600+
CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION);
601+
ss_blockhash << pblockindex->GetBlockHash();
602+
req->WriteHeader("Content-Type", "application/octet-stream");
603+
req->WriteReply(HTTP_OK, ss_blockhash.str());
604+
return true;
605+
}
606+
case RetFormat::HEX: {
607+
req->WriteHeader("Content-Type", "text/plain");
608+
req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
609+
return true;
610+
}
611+
case RetFormat::JSON: {
612+
req->WriteHeader("Content-Type", "application/json");
613+
UniValue resp = UniValue(UniValue::VOBJ);
614+
resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
615+
req->WriteReply(HTTP_OK, resp.write() + "\n");
616+
return true;
617+
}
618+
default: {
619+
return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
620+
}
621+
}
622+
}
623+
578624
static const struct {
579625
const char* prefix;
580626
bool (*handler)(HTTPRequest* req, const std::string& strReq);
@@ -587,6 +633,7 @@ static const struct {
587633
{"/rest/mempool/contents", rest_mempool_contents},
588634
{"/rest/headers/", rest_headers},
589635
{"/rest/getutxos", rest_getutxos},
636+
{"/rest/blockhashbyheight/", rest_blockhash_by_height},
590637
};
591638

592639
void StartREST()

test/functional/interface_rest.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def run_test(self):
198198
self.nodes[0].generate(1) # generate block to not affect upcoming tests
199199
self.sync_all()
200200

201-
self.log.info("Test the /block and /headers URIs")
201+
self.log.info("Test the /block, /blockhashbyheight and /headers URIs")
202202
bb_hash = self.nodes[0].getbestblockhash()
203203

204204
# Check result if block does not exists
@@ -237,6 +237,23 @@ def run_test(self):
237237
# Check json format
238238
block_json_obj = self.test_rest_request("/block/{}".format(bb_hash))
239239
assert_equal(block_json_obj['hash'], bb_hash)
240+
assert_equal(self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']))['blockhash'], bb_hash)
241+
242+
# Check hex/bin format
243+
resp_hex = self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']), req_type=ReqType.HEX, ret_type=RetType.OBJ)
244+
assert_equal(resp_hex.read().decode('utf-8').rstrip(), bb_hash)
245+
resp_bytes = self.test_rest_request("/blockhashbyheight/{}".format(block_json_obj['height']), req_type=ReqType.BIN, ret_type=RetType.BYTES)
246+
blockhash = binascii.hexlify(resp_bytes[::-1]).decode('utf-8')
247+
assert_equal(blockhash, bb_hash)
248+
249+
# Check invalid blockhashbyheight requests
250+
resp = self.test_rest_request("/blockhashbyheight/abc", ret_type=RetType.OBJ, status=400)
251+
assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: abc")
252+
resp = self.test_rest_request("/blockhashbyheight/1000000", ret_type=RetType.OBJ, status=404)
253+
assert_equal(resp.read().decode('utf-8').rstrip(), "Block height out of range")
254+
resp = self.test_rest_request("/blockhashbyheight/-1", ret_type=RetType.OBJ, status=400)
255+
assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: -1")
256+
self.test_rest_request("/blockhashbyheight/", ret_type=RetType.OBJ, status=400)
240257

241258
# Compare with json block header
242259
json_obj = self.test_rest_request("/headers/1/{}".format(bb_hash))

0 commit comments

Comments
 (0)