Changeset View
Changeset View
Standalone View
Standalone View
src/rest.cpp
Show All 21 Lines | |||||
#include <boost/algorithm/string.hpp> | #include <boost/algorithm/string.hpp> | ||||
#include <univalue.h> | #include <univalue.h> | ||||
// Allow a max of 15 outpoints to be queried at once. | // Allow a max of 15 outpoints to be queried at once. | ||||
static const size_t MAX_GETUTXOS_OUTPOINTS = 15; | static const size_t MAX_GETUTXOS_OUTPOINTS = 15; | ||||
enum RetFormat { | enum class RetFormat { | ||||
RF_UNDEF, | UNDEF, | ||||
RF_BINARY, | BINARY, | ||||
RF_HEX, | HEX, | ||||
RF_JSON, | JSON, | ||||
}; | }; | ||||
static const struct { | static const struct { | ||||
enum RetFormat rf; | enum RetFormat rf; | ||||
const char *name; | const char *name; | ||||
} rf_names[] = { | } rf_names[] = { | ||||
{RF_UNDEF, ""}, | {RetFormat::UNDEF, ""}, | ||||
{RF_BINARY, "bin"}, | {RetFormat::BINARY, "bin"}, | ||||
{RF_HEX, "hex"}, | {RetFormat::HEX, "hex"}, | ||||
{RF_JSON, "json"}, | {RetFormat::JSON, "json"}, | ||||
}; | }; | ||||
struct CCoin { | struct CCoin { | ||||
uint32_t nHeight; | uint32_t nHeight; | ||||
CTxOut out; | CTxOut out; | ||||
CCoin() : nHeight(0) {} | CCoin() : nHeight(0) {} | ||||
explicit CCoin(Coin in) | explicit CCoin(Coin in) | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | static bool rest_headers(Config &config, HTTPRequest *req, | ||||
} | } | ||||
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); | ||||
for (const CBlockIndex *pindex : headers) { | for (const CBlockIndex *pindex : headers) { | ||||
ssHeader << pindex->GetBlockHeader(); | ssHeader << pindex->GetBlockHeader(); | ||||
} | } | ||||
switch (rf) { | switch (rf) { | ||||
case RF_BINARY: { | case RetFormat::BINARY: { | ||||
std::string binaryHeader = ssHeader.str(); | std::string binaryHeader = ssHeader.str(); | ||||
req->WriteHeader("Content-Type", "application/octet-stream"); | req->WriteHeader("Content-Type", "application/octet-stream"); | ||||
req->WriteReply(HTTP_OK, binaryHeader); | req->WriteReply(HTTP_OK, binaryHeader); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_HEX: { | case RetFormat::HEX: { | ||||
std::string strHex = | std::string strHex = | ||||
HexStr(ssHeader.begin(), ssHeader.end()) + "\n"; | HexStr(ssHeader.begin(), ssHeader.end()) + "\n"; | ||||
req->WriteHeader("Content-Type", "text/plain"); | req->WriteHeader("Content-Type", "text/plain"); | ||||
req->WriteReply(HTTP_OK, strHex); | req->WriteReply(HTTP_OK, strHex); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
UniValue jsonHeaders(UniValue::VARR); | UniValue jsonHeaders(UniValue::VARR); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const CBlockIndex *pindex : headers) { | for (const CBlockIndex *pindex : headers) { | ||||
jsonHeaders.push_back(blockheaderToJSON(pindex)); | jsonHeaders.push_back(blockheaderToJSON(pindex)); | ||||
} | } | ||||
} | } | ||||
std::string strJSON = jsonHeaders.write() + "\n"; | std::string strJSON = jsonHeaders.write() + "\n"; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | CBlockIndex *pblockindex = nullptr; | ||||
} | } | ||||
} | } | ||||
CDataStream ssBlock(SER_NETWORK, | CDataStream ssBlock(SER_NETWORK, | ||||
PROTOCOL_VERSION | RPCSerializationFlags()); | PROTOCOL_VERSION | RPCSerializationFlags()); | ||||
ssBlock << block; | ssBlock << block; | ||||
switch (rf) { | switch (rf) { | ||||
case RF_BINARY: { | case RetFormat::BINARY: { | ||||
std::string binaryBlock = ssBlock.str(); | std::string binaryBlock = ssBlock.str(); | ||||
req->WriteHeader("Content-Type", "application/octet-stream"); | req->WriteHeader("Content-Type", "application/octet-stream"); | ||||
req->WriteReply(HTTP_OK, binaryBlock); | req->WriteReply(HTTP_OK, binaryBlock); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_HEX: { | case RetFormat::HEX: { | ||||
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n"; | std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n"; | ||||
req->WriteHeader("Content-Type", "text/plain"); | req->WriteHeader("Content-Type", "text/plain"); | ||||
req->WriteReply(HTTP_OK, strHex); | req->WriteReply(HTTP_OK, strHex); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
UniValue objBlock; | UniValue objBlock; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
objBlock = | objBlock = | ||||
blockToJSON(config, block, pblockindex, showTxDetails); | blockToJSON(config, block, pblockindex, showTxDetails); | ||||
} | } | ||||
std::string strJSON = objBlock.write() + "\n"; | std::string strJSON = objBlock.write() + "\n"; | ||||
req->WriteHeader("Content-Type", "application/json"); | req->WriteHeader("Content-Type", "application/json"); | ||||
Show All 28 Lines | static bool rest_chaininfo(Config &config, HTTPRequest *req, | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
std::string param; | std::string param; | ||||
const RetFormat rf = ParseDataFormat(param, strURIPart); | const RetFormat rf = ParseDataFormat(param, strURIPart); | ||||
switch (rf) { | switch (rf) { | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
JSONRPCRequest jsonRequest; | JSONRPCRequest jsonRequest; | ||||
jsonRequest.params = UniValue(UniValue::VARR); | jsonRequest.params = UniValue(UniValue::VARR); | ||||
UniValue chainInfoObject = getblockchaininfo(config, jsonRequest); | UniValue chainInfoObject = getblockchaininfo(config, jsonRequest); | ||||
std::string strJSON = chainInfoObject.write() + "\n"; | std::string strJSON = chainInfoObject.write() + "\n"; | ||||
req->WriteHeader("Content-Type", "application/json"); | req->WriteHeader("Content-Type", "application/json"); | ||||
req->WriteReply(HTTP_OK, strJSON); | req->WriteReply(HTTP_OK, strJSON); | ||||
return true; | return true; | ||||
} | } | ||||
Show All 13 Lines | static bool rest_mempool_info(Config &config, HTTPRequest *req, | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
std::string param; | std::string param; | ||||
const RetFormat rf = ParseDataFormat(param, strURIPart); | const RetFormat rf = ParseDataFormat(param, strURIPart); | ||||
switch (rf) { | switch (rf) { | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
UniValue mempoolInfoObject = mempoolInfoToJSON(); | UniValue mempoolInfoObject = mempoolInfoToJSON(); | ||||
std::string strJSON = mempoolInfoObject.write() + "\n"; | std::string strJSON = mempoolInfoObject.write() + "\n"; | ||||
req->WriteHeader("Content-Type", "application/json"); | req->WriteHeader("Content-Type", "application/json"); | ||||
req->WriteReply(HTTP_OK, strJSON); | req->WriteReply(HTTP_OK, strJSON); | ||||
return true; | return true; | ||||
} | } | ||||
default: { | default: { | ||||
Show All 12 Lines | static bool rest_mempool_contents(Config &config, HTTPRequest *req, | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
std::string param; | std::string param; | ||||
const RetFormat rf = ParseDataFormat(param, strURIPart); | const RetFormat rf = ParseDataFormat(param, strURIPart); | ||||
switch (rf) { | switch (rf) { | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
UniValue mempoolObject = mempoolToJSON(true); | UniValue mempoolObject = mempoolToJSON(true); | ||||
std::string strJSON = mempoolObject.write() + "\n"; | std::string strJSON = mempoolObject.write() + "\n"; | ||||
req->WriteHeader("Content-Type", "application/json"); | req->WriteHeader("Content-Type", "application/json"); | ||||
req->WriteReply(HTTP_OK, strJSON); | req->WriteReply(HTTP_OK, strJSON); | ||||
return true; | return true; | ||||
} | } | ||||
default: { | default: { | ||||
Show All 28 Lines | static bool rest_tx(Config &config, HTTPRequest *req, | ||||
if (!GetTransaction(config, txid, tx, hashBlock, true)) { | if (!GetTransaction(config, txid, tx, hashBlock, true)) { | ||||
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); | return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); | ||||
} | } | ||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); | CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); | ||||
ssTx << tx; | ssTx << tx; | ||||
switch (rf) { | switch (rf) { | ||||
case RF_BINARY: { | case RetFormat::BINARY: { | ||||
std::string binaryTx = ssTx.str(); | std::string binaryTx = ssTx.str(); | ||||
req->WriteHeader("Content-Type", "application/octet-stream"); | req->WriteHeader("Content-Type", "application/octet-stream"); | ||||
req->WriteReply(HTTP_OK, binaryTx); | req->WriteReply(HTTP_OK, binaryTx); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_HEX: { | case RetFormat::HEX: { | ||||
std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n"; | std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n"; | ||||
req->WriteHeader("Content-Type", "text/plain"); | req->WriteHeader("Content-Type", "text/plain"); | ||||
req->WriteReply(HTTP_OK, strHex); | req->WriteReply(HTTP_OK, strHex); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
UniValue objTx(UniValue::VOBJ); | UniValue objTx(UniValue::VOBJ); | ||||
TxToUniv(*tx, hashBlock, objTx); | TxToUniv(*tx, hashBlock, objTx); | ||||
std::string strJSON = objTx.write() + "\n"; | std::string strJSON = objTx.write() + "\n"; | ||||
req->WriteHeader("Content-Type", "application/json"); | req->WriteHeader("Content-Type", "application/json"); | ||||
req->WriteReply(HTTP_OK, strJSON); | req->WriteReply(HTTP_OK, strJSON); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | if (uriParts.size() > 0) { | ||||
if (vOutPoints.size() > 0) { | if (vOutPoints.size() > 0) { | ||||
fInputParsed = true; | fInputParsed = true; | ||||
} else { | } else { | ||||
return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); | return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); | ||||
} | } | ||||
} | } | ||||
switch (rf) { | switch (rf) { | ||||
case RF_HEX: { | case RetFormat::HEX: { | ||||
// convert hex to bin, continue then with bin part | // convert hex to bin, continue then with bin part | ||||
std::vector<uint8_t> strRequestV = ParseHex(strRequestMutable); | std::vector<uint8_t> strRequestV = ParseHex(strRequestMutable); | ||||
strRequestMutable.assign(strRequestV.begin(), strRequestV.end()); | strRequestMutable.assign(strRequestV.begin(), strRequestV.end()); | ||||
} | } | ||||
// FALLTHROUGH | // FALLTHROUGH | ||||
case RF_BINARY: { | case RetFormat::BINARY: { | ||||
try { | try { | ||||
// deserialize only if user sent a request | // deserialize only if user sent a request | ||||
if (strRequestMutable.size() > 0) { | if (strRequestMutable.size() > 0) { | ||||
// don't allow sending input over URI and HTTP RAW DATA | // don't allow sending input over URI and HTTP RAW DATA | ||||
if (fInputParsed) { | if (fInputParsed) { | ||||
return RESTERR(req, HTTP_BAD_REQUEST, | return RESTERR(req, HTTP_BAD_REQUEST, | ||||
"Combination of URI scheme inputs and " | "Combination of URI scheme inputs and " | ||||
"raw post data is not allowed"); | "raw post data is not allowed"); | ||||
} | } | ||||
CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); | CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); | ||||
oss << strRequestMutable; | oss << strRequestMutable; | ||||
oss >> fCheckMemPool; | oss >> fCheckMemPool; | ||||
oss >> vOutPoints; | oss >> vOutPoints; | ||||
} | } | ||||
} catch (const std::ios_base::failure &e) { | } catch (const std::ios_base::failure &e) { | ||||
// abort in case of unreadable binary data | // abort in case of unreadable binary data | ||||
return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); | return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
if (!fInputParsed) { | if (!fInputParsed) { | ||||
return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); | return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
default: { | default: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: " + | "output format not found (available: " + | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | bitmap.resize((vOutPoints.size() + 7) / 8); | ||||
// form a binary string representation (human-readable for json | // form a binary string representation (human-readable for json | ||||
// output) | // output) | ||||
bitmapStringRepresentation.append(hit ? "1" : "0"); | bitmapStringRepresentation.append(hit ? "1" : "0"); | ||||
bitmap[i / 8] |= ((uint8_t)hit) << (i % 8); | bitmap[i / 8] |= ((uint8_t)hit) << (i % 8); | ||||
} | } | ||||
} | } | ||||
switch (rf) { | switch (rf) { | ||||
case RF_BINARY: { | case RetFormat::BINARY: { | ||||
// serialize data | // serialize data | ||||
// use exact same output as mentioned in Bip64 | // use exact same output as mentioned in Bip64 | ||||
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); | ||||
ssGetUTXOResponse << chainActive.Height() | ssGetUTXOResponse << chainActive.Height() | ||||
<< chainActive.Tip()->GetBlockHash() << bitmap | << chainActive.Tip()->GetBlockHash() << bitmap | ||||
<< outs; | << outs; | ||||
std::string ssGetUTXOResponseString = ssGetUTXOResponse.str(); | std::string ssGetUTXOResponseString = ssGetUTXOResponse.str(); | ||||
req->WriteHeader("Content-Type", "application/octet-stream"); | req->WriteHeader("Content-Type", "application/octet-stream"); | ||||
req->WriteReply(HTTP_OK, ssGetUTXOResponseString); | req->WriteReply(HTTP_OK, ssGetUTXOResponseString); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_HEX: { | case RetFormat::HEX: { | ||||
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); | ||||
ssGetUTXOResponse << chainActive.Height() | ssGetUTXOResponse << chainActive.Height() | ||||
<< chainActive.Tip()->GetBlockHash() << bitmap | << chainActive.Tip()->GetBlockHash() << bitmap | ||||
<< outs; | << outs; | ||||
std::string strHex = | std::string strHex = | ||||
HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + | HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + | ||||
"\n"; | "\n"; | ||||
req->WriteHeader("Content-Type", "text/plain"); | req->WriteHeader("Content-Type", "text/plain"); | ||||
req->WriteReply(HTTP_OK, strHex); | req->WriteReply(HTTP_OK, strHex); | ||||
return true; | return true; | ||||
} | } | ||||
case RF_JSON: { | case RetFormat::JSON: { | ||||
UniValue objGetUTXOResponse(UniValue::VOBJ); | UniValue objGetUTXOResponse(UniValue::VOBJ); | ||||
// pack in some essentials | // pack in some essentials | ||||
// use more or less the same output as mentioned in Bip64 | // use more or less the same output as mentioned in Bip64 | ||||
objGetUTXOResponse.pushKV("chainHeight", chainActive.Height()); | objGetUTXOResponse.pushKV("chainHeight", chainActive.Height()); | ||||
objGetUTXOResponse.pushKV( | objGetUTXOResponse.pushKV( | ||||
"chaintipHash", chainActive.Tip()->GetBlockHash().GetHex()); | "chaintipHash", chainActive.Tip()->GetBlockHash().GetHex()); | ||||
objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); | objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); | ||||
▲ Show 20 Lines • Show All 64 Lines • Show Last 20 Lines |