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
This commit is contained in:
Jonas Schnelli
2019-01-22 19:58:43 -10:00
3 changed files with 70 additions and 1 deletions

View File

@@ -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. 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. 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 #### Chaininfos
`GET /rest/chaininfo.json` `GET /rest/chaininfo.json`

View File

@@ -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 { static const struct {
const char* prefix; const char* prefix;
bool (*handler)(HTTPRequest* req, const std::string& strReq); bool (*handler)(HTTPRequest* req, const std::string& strReq);
@@ -587,6 +633,7 @@ static const struct {
{"/rest/mempool/contents", rest_mempool_contents}, {"/rest/mempool/contents", rest_mempool_contents},
{"/rest/headers/", rest_headers}, {"/rest/headers/", rest_headers},
{"/rest/getutxos", rest_getutxos}, {"/rest/getutxos", rest_getutxos},
{"/rest/blockhashbyheight/", rest_blockhash_by_height},
}; };
void StartREST() void StartREST()

View File

@@ -198,7 +198,7 @@ class RESTTest (BitcoinTestFramework):
self.nodes[0].generate(1) # generate block to not affect upcoming tests self.nodes[0].generate(1) # generate block to not affect upcoming tests
self.sync_all() 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() bb_hash = self.nodes[0].getbestblockhash()
# Check result if block does not exists # Check result if block does not exists
@@ -237,6 +237,23 @@ class RESTTest (BitcoinTestFramework):
# Check json format # Check json format
block_json_obj = self.test_rest_request("/block/{}".format(bb_hash)) block_json_obj = self.test_rest_request("/block/{}".format(bb_hash))
assert_equal(block_json_obj['hash'], 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 # Compare with json block header
json_obj = self.test_rest_request("/headers/1/{}".format(bb_hash)) json_obj = self.test_rest_request("/headers/1/{}".format(bb_hash))