mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-18 13:17:55 +02:00
Merge 27cfaeed1e into merged_master (Bitcoin PR bitcoin/bitcoin#24098)
This commit is contained in:
commit
18d0ebdd77
11 changed files with 334 additions and 96 deletions
171
src/rest.cpp
171
src/rest.cpp
|
|
@ -3,6 +3,8 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <rest.h>
|
||||
|
||||
#include <blockfilter.h>
|
||||
#include <chain.h>
|
||||
#include <chainparams.h>
|
||||
|
|
@ -28,6 +30,7 @@
|
|||
#include <version.h>
|
||||
|
||||
#include <any>
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
|
@ -41,21 +44,14 @@ using node::ReadBlockFromDisk;
|
|||
static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
|
||||
static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000;
|
||||
|
||||
enum class RetFormat {
|
||||
UNDEF,
|
||||
BINARY,
|
||||
HEX,
|
||||
JSON,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
RetFormat rf;
|
||||
RESTResponseFormat rf;
|
||||
const char* name;
|
||||
} rf_names[] = {
|
||||
{RetFormat::UNDEF, ""},
|
||||
{RetFormat::BINARY, "bin"},
|
||||
{RetFormat::HEX, "hex"},
|
||||
{RetFormat::JSON, "json"},
|
||||
{RESTResponseFormat::UNDEF, ""},
|
||||
{RESTResponseFormat::BINARY, "bin"},
|
||||
{RESTResponseFormat::HEX, "hex"},
|
||||
{RESTResponseFormat::JSON, "json"},
|
||||
};
|
||||
|
||||
struct CCoin {
|
||||
|
|
@ -138,25 +134,28 @@ static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
|
|||
return node_context->chainman.get();
|
||||
}
|
||||
|
||||
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
|
||||
RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq)
|
||||
{
|
||||
const std::string::size_type pos = strReq.rfind('.');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
param = strReq;
|
||||
// Remove query string (if any, separated with '?') as it should not interfere with
|
||||
// parsing param and data format
|
||||
param = strReq.substr(0, strReq.rfind('?'));
|
||||
const std::string::size_type pos_format{param.rfind('.')};
|
||||
|
||||
// No format string is found
|
||||
if (pos_format == std::string::npos) {
|
||||
return rf_names[0].rf;
|
||||
}
|
||||
|
||||
param = strReq.substr(0, pos);
|
||||
const std::string suff(strReq, pos + 1);
|
||||
|
||||
// Match format string to available formats
|
||||
const std::string suffix(param, pos_format + 1);
|
||||
for (const auto& rf_name : rf_names) {
|
||||
if (suff == rf_name.name)
|
||||
if (suffix == rf_name.name) {
|
||||
param.erase(pos_format);
|
||||
return rf_name.rf;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no suffix is found, return original string. */
|
||||
param = strReq;
|
||||
// If no suffix is found, return RESTResponseFormat::UNDEF and original string without query string
|
||||
return rf_names[0].rf;
|
||||
}
|
||||
|
||||
|
|
@ -192,19 +191,29 @@ static bool rest_headers(const std::any& context,
|
|||
if (!CheckWarmup(req))
|
||||
return false;
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
std::vector<std::string> path;
|
||||
boost::split(path, param, boost::is_any_of("/"));
|
||||
|
||||
if (path.size() != 2)
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
|
||||
|
||||
const auto parsed_count{ToIntegral<size_t>(path[0])};
|
||||
if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, path[0]));
|
||||
std::string raw_count;
|
||||
std::string hashStr;
|
||||
if (path.size() == 2) {
|
||||
// deprecated path: /rest/headers/<count>/<hash>
|
||||
hashStr = path[1];
|
||||
raw_count = path[0];
|
||||
} else if (path.size() == 1) {
|
||||
// new path with query parameter: /rest/headers/<hash>?count=<count>
|
||||
hashStr = path[0];
|
||||
raw_count = req->GetQueryParameter("count").value_or("5");
|
||||
} else {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
|
||||
}
|
||||
|
||||
const auto parsed_count{ToIntegral<size_t>(raw_count)};
|
||||
if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
|
||||
}
|
||||
|
||||
std::string hashStr = path[1];
|
||||
uint256 hash;
|
||||
if (!ParseHashStr(hashStr, hash))
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
||||
|
|
@ -230,7 +239,7 @@ static bool rest_headers(const std::any& context,
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
|
||||
for (const CBlockIndex *pindex : headers) {
|
||||
if (pindex->trimmed()) {
|
||||
|
|
@ -248,7 +257,7 @@ static bool rest_headers(const std::any& context,
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
|
||||
for (const CBlockIndex *pindex : headers) {
|
||||
if (pindex->trimmed()) {
|
||||
|
|
@ -266,7 +275,7 @@ static bool rest_headers(const std::any& context,
|
|||
req->WriteReply(HTTP_OK, strHex);
|
||||
return true;
|
||||
}
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue jsonHeaders(UniValue::VARR);
|
||||
for (const CBlockIndex *pindex : headers) {
|
||||
jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
|
||||
|
|
@ -290,7 +299,7 @@ static bool rest_block(const std::any& context,
|
|||
if (!CheckWarmup(req))
|
||||
return false;
|
||||
std::string hashStr;
|
||||
const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);
|
||||
|
||||
uint256 hash;
|
||||
if (!ParseHashStr(hashStr, hash))
|
||||
|
|
@ -318,7 +327,7 @@ static bool rest_block(const std::any& context,
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
|
||||
ssBlock << block;
|
||||
std::string binaryBlock = ssBlock.str();
|
||||
|
|
@ -327,7 +336,7 @@ static bool rest_block(const std::any& context,
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
|
||||
ssBlock << block;
|
||||
std::string strHex = HexStr(ssBlock) + "\n";
|
||||
|
|
@ -336,7 +345,7 @@ static bool rest_block(const std::any& context,
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue objBlock = blockToJSON(block, tip, pblockindex, tx_verbosity);
|
||||
std::string strJSON = objBlock.write() + "\n";
|
||||
req->WriteHeader("Content-Type", "application/json");
|
||||
|
|
@ -365,17 +374,32 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
|
|||
if (!CheckWarmup(req)) return false;
|
||||
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
|
||||
std::vector<std::string> uri_parts;
|
||||
boost::split(uri_parts, param, boost::is_any_of("/"));
|
||||
if (uri_parts.size() != 3) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>");
|
||||
std::string raw_count;
|
||||
std::string raw_blockhash;
|
||||
if (uri_parts.size() == 3) {
|
||||
// deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>
|
||||
raw_blockhash = uri_parts[2];
|
||||
raw_count = uri_parts[1];
|
||||
} else if (uri_parts.size() == 2) {
|
||||
// new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
|
||||
raw_blockhash = uri_parts[1];
|
||||
raw_count = req->GetQueryParameter("count").value_or("5");
|
||||
} else {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
|
||||
}
|
||||
|
||||
const auto parsed_count{ToIntegral<size_t>(raw_count)};
|
||||
if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
|
||||
}
|
||||
|
||||
uint256 block_hash;
|
||||
if (!ParseHashStr(uri_parts[2], block_hash)) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[2]);
|
||||
if (!ParseHashStr(raw_blockhash, block_hash)) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash);
|
||||
}
|
||||
|
||||
BlockFilterType filtertype;
|
||||
|
|
@ -388,11 +412,6 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
|
|||
return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
|
||||
}
|
||||
|
||||
const auto parsed_count{ToIntegral<size_t>(uri_parts[1])};
|
||||
if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, uri_parts[1]));
|
||||
}
|
||||
|
||||
std::vector<const CBlockIndex*> headers;
|
||||
headers.reserve(*parsed_count);
|
||||
{
|
||||
|
|
@ -431,7 +450,7 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
CDataStream ssHeader{SER_NETWORK, PROTOCOL_VERSION};
|
||||
for (const uint256& header : filter_headers) {
|
||||
ssHeader << header;
|
||||
|
|
@ -442,7 +461,7 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
|
|||
req->WriteReply(HTTP_OK, binaryHeader);
|
||||
return true;
|
||||
}
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
CDataStream ssHeader{SER_NETWORK, PROTOCOL_VERSION};
|
||||
for (const uint256& header : filter_headers) {
|
||||
ssHeader << header;
|
||||
|
|
@ -453,7 +472,7 @@ static bool rest_filter_header(const std::any& context, HTTPRequest* req, const
|
|||
req->WriteReply(HTTP_OK, strHex);
|
||||
return true;
|
||||
}
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue jsonHeaders(UniValue::VARR);
|
||||
for (const uint256& header : filter_headers) {
|
||||
jsonHeaders.push_back(header.GetHex());
|
||||
|
|
@ -475,7 +494,7 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
|
|||
if (!CheckWarmup(req)) return false;
|
||||
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
|
||||
// request is sent over URI scheme /rest/blockfilter/filtertype/blockhash
|
||||
std::vector<std::string> uri_parts;
|
||||
|
|
@ -531,7 +550,7 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
CDataStream ssResp{SER_NETWORK, PROTOCOL_VERSION};
|
||||
ssResp << filter;
|
||||
|
||||
|
|
@ -540,7 +559,7 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
|
|||
req->WriteReply(HTTP_OK, binaryResp);
|
||||
return true;
|
||||
}
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
CDataStream ssResp{SER_NETWORK, PROTOCOL_VERSION};
|
||||
ssResp << filter;
|
||||
|
||||
|
|
@ -549,7 +568,7 @@ static bool rest_block_filter(const std::any& context, HTTPRequest* req, const s
|
|||
req->WriteReply(HTTP_OK, strHex);
|
||||
return true;
|
||||
}
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
|
||||
std::string strJSON = ret.write() + "\n";
|
||||
|
|
@ -571,10 +590,10 @@ static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std:
|
|||
if (!CheckWarmup(req))
|
||||
return false;
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
JSONRPCRequest jsonRequest;
|
||||
jsonRequest.context = context;
|
||||
jsonRequest.params = UniValue(UniValue::VARR);
|
||||
|
|
@ -597,10 +616,10 @@ static bool rest_mempool_info(const std::any& context, HTTPRequest* req, const s
|
|||
const CTxMemPool* mempool = GetMemPool(context, req);
|
||||
if (!mempool) return false;
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
|
||||
|
||||
std::string strJSON = mempoolInfoObject.write() + "\n";
|
||||
|
|
@ -620,10 +639,10 @@ static bool rest_mempool_contents(const std::any& context, HTTPRequest* req, con
|
|||
const CTxMemPool* mempool = GetMemPool(context, req);
|
||||
if (!mempool) return false;
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue mempoolObject = MempoolToJSON(*mempool, true);
|
||||
|
||||
std::string strJSON = mempoolObject.write() + "\n";
|
||||
|
|
@ -642,7 +661,7 @@ static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string
|
|||
if (!CheckWarmup(req))
|
||||
return false;
|
||||
std::string hashStr;
|
||||
const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);
|
||||
|
||||
uint256 hash;
|
||||
if (!ParseHashStr(hashStr, hash))
|
||||
|
|
@ -661,7 +680,7 @@ static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
|
||||
ssTx << tx;
|
||||
|
||||
|
|
@ -671,7 +690,7 @@ static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
|
||||
ssTx << tx;
|
||||
|
||||
|
|
@ -681,7 +700,7 @@ static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue objTx(UniValue::VOBJ);
|
||||
TxToUniv(*tx, /*block_hash=*/hashBlock, /*entry=*/ objTx);
|
||||
std::string strJSON = objTx.write() + "\n";
|
||||
|
|
@ -701,7 +720,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
|||
if (!CheckWarmup(req))
|
||||
return false;
|
||||
std::string param;
|
||||
const RetFormat rf = ParseDataFormat(param, strURIPart);
|
||||
const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
|
||||
|
||||
std::vector<std::string> uriParts;
|
||||
if (param.length() > 1)
|
||||
|
|
@ -748,14 +767,14 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
// convert hex to bin, continue then with bin part
|
||||
std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
|
||||
strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
try {
|
||||
//deserialize only if user sent a request
|
||||
if (strRequestMutable.size() > 0)
|
||||
|
|
@ -775,7 +794,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
|||
break;
|
||||
}
|
||||
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
if (!fInputParsed)
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
|
||||
break;
|
||||
|
|
@ -829,7 +848,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
|||
}
|
||||
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
// serialize data
|
||||
// use exact same output as mentioned in Bip64
|
||||
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
|
@ -841,7 +860,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::HEX: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
|
||||
std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
|
||||
|
|
@ -851,7 +870,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
|||
return true;
|
||||
}
|
||||
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
UniValue objGetUTXOResponse(UniValue::VOBJ);
|
||||
|
||||
// pack in some essentials
|
||||
|
|
@ -893,7 +912,7 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
|
|||
{
|
||||
if (!CheckWarmup(req)) return false;
|
||||
std::string height_str;
|
||||
const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
|
||||
const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part);
|
||||
|
||||
int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
|
||||
if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
|
||||
|
|
@ -913,19 +932,19 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
|
|||
pblockindex = active_chain[blockheight];
|
||||
}
|
||||
switch (rf) {
|
||||
case RetFormat::BINARY: {
|
||||
case RESTResponseFormat::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: {
|
||||
case RESTResponseFormat::HEX: {
|
||||
req->WriteHeader("Content-Type", "text/plain");
|
||||
req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
|
||||
return true;
|
||||
}
|
||||
case RetFormat::JSON: {
|
||||
case RESTResponseFormat::JSON: {
|
||||
req->WriteHeader("Content-Type", "application/json");
|
||||
UniValue resp = UniValue(UniValue::VOBJ);
|
||||
resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue