Changeset View
Changeset View
Standalone View
Standalone View
src/rest.cpp
Show All 13 Lines | |||||
#include <primitives/block.h> | #include <primitives/block.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <rpc/blockchain.h> | #include <rpc/blockchain.h> | ||||
#include <rpc/protocol.h> | #include <rpc/protocol.h> | ||||
#include <rpc/server.h> | #include <rpc/server.h> | ||||
#include <streams.h> | #include <streams.h> | ||||
#include <sync.h> | #include <sync.h> | ||||
#include <txmempool.h> | #include <txmempool.h> | ||||
#include <util/ref.h> | |||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <validation.h> | #include <validation.h> | ||||
#include <version.h> | #include <version.h> | ||||
#include <boost/algorithm/string.hpp> | #include <boost/algorithm/string.hpp> | ||||
#include <univalue.h> | #include <univalue.h> | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
* Get the node context mempool. | * Get the node context mempool. | ||||
* | * | ||||
* Set the HTTP error and return nullptr if node context | * Set the HTTP error and return nullptr if node context | ||||
* mempool is not found. | * mempool is not found. | ||||
* | * | ||||
* @param[in] req the HTTP request | * @param[in] req the HTTP request | ||||
* return pointer to the mempool or nullptr if no mempool found | * return pointer to the mempool or nullptr if no mempool found | ||||
*/ | */ | ||||
static CTxMemPool *GetMemPool(HTTPRequest *req) { | static CTxMemPool *GetMemPool(const util::Ref &context, HTTPRequest *req) { | ||||
if (!g_rpc_node || !g_rpc_node->mempool) { | NodeContext *node = | ||||
context.Has<NodeContext>() ? &context.Get<NodeContext>() : nullptr; | |||||
if (!node || !node->mempool) { | |||||
RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found"); | RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found"); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return g_rpc_node->mempool; | return node->mempool; | ||||
} | } | ||||
static RetFormat ParseDataFormat(std::string ¶m, | static RetFormat ParseDataFormat(std::string ¶m, | ||||
const std::string &strReq) { | const std::string &strReq) { | ||||
const std::string::size_type pos = strReq.rfind('.'); | const std::string::size_type pos = strReq.rfind('.'); | ||||
if (pos == std::string::npos) { | if (pos == std::string::npos) { | ||||
param = strReq; | param = strReq; | ||||
return rf_names[0].rf; | return rf_names[0].rf; | ||||
Show All 35 Lines | static bool CheckWarmup(HTTPRequest *req) { | ||||
if (RPCIsInWarmup(&statusmessage)) { | if (RPCIsInWarmup(&statusmessage)) { | ||||
return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, | return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, | ||||
"Service temporarily unavailable: " + statusmessage); | "Service temporarily unavailable: " + statusmessage); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
static bool rest_headers(Config &config, HTTPRequest *req, | static bool rest_headers(Config &config, const util::Ref &context, | ||||
const std::string &strURIPart) { | HTTPRequest *req, const std::string &strURIPart) { | ||||
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); | ||||
std::vector<std::string> path; | std::vector<std::string> path; | ||||
boost::split(path, param, boost::is_any_of("/")); | boost::split(path, param, boost::is_any_of("/")); | ||||
▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | switch (rf) { | ||||
default: { | default: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: " + | "output format not found (available: " + | ||||
AvailableDataFormatsString() + ")"); | AvailableDataFormatsString() + ")"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static bool rest_block_extended(Config &config, HTTPRequest *req, | static bool rest_block_extended(Config &config, const util::Ref &context, | ||||
HTTPRequest *req, | |||||
const std::string &strURIPart) { | const std::string &strURIPart) { | ||||
return rest_block(config, req, strURIPart, true); | return rest_block(config, req, strURIPart, true); | ||||
} | } | ||||
static bool rest_block_notxdetails(Config &config, HTTPRequest *req, | static bool rest_block_notxdetails(Config &config, const util::Ref &context, | ||||
HTTPRequest *req, | |||||
const std::string &strURIPart) { | const std::string &strURIPart) { | ||||
return rest_block(config, req, strURIPart, false); | return rest_block(config, req, strURIPart, false); | ||||
} | } | ||||
static bool rest_chaininfo(Config &config, HTTPRequest *req, | static bool rest_chaininfo(Config &config, const util::Ref &context, | ||||
const std::string &strURIPart) { | HTTPRequest *req, const std::string &strURIPart) { | ||||
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 RetFormat::JSON: { | case RetFormat::JSON: { | ||||
JSONRPCRequest jsonRequest; | JSONRPCRequest jsonRequest(context); | ||||
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; | ||||
} | } | ||||
default: { | default: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: json)"); | "output format not found (available: json)"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static bool rest_mempool_info(Config &config, HTTPRequest *req, | static bool rest_mempool_info(Config &config, const util::Ref &context, | ||||
const std::string &strURIPart) { | HTTPRequest *req, const std::string &strURIPart) { | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
const CTxMemPool *mempool = GetMemPool(req); | const CTxMemPool *mempool = GetMemPool(context, req); | ||||
if (!mempool) { | if (!mempool) { | ||||
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 RetFormat::JSON: { | case RetFormat::JSON: { | ||||
UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool); | UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool); | ||||
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: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: json)"); | "output format not found (available: json)"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static bool rest_mempool_contents(Config &config, HTTPRequest *req, | static bool rest_mempool_contents(Config &config, const util::Ref &context, | ||||
HTTPRequest *req, | |||||
const std::string &strURIPart) { | const std::string &strURIPart) { | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
const CTxMemPool *mempool = GetMemPool(req); | const CTxMemPool *mempool = GetMemPool(context, req); | ||||
if (!mempool) { | if (!mempool) { | ||||
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 RetFormat::JSON: { | case RetFormat::JSON: { | ||||
UniValue mempoolObject = MempoolToJSON(*mempool, true); | UniValue mempoolObject = MempoolToJSON(*mempool, 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: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: json)"); | "output format not found (available: json)"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static bool rest_tx(Config &config, HTTPRequest *req, | static bool rest_tx(Config &config, const util::Ref &context, HTTPRequest *req, | ||||
const std::string &strURIPart) { | const std::string &strURIPart) { | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
std::string hashStr; | std::string hashStr; | ||||
const RetFormat rf = ParseDataFormat(hashStr, strURIPart); | const RetFormat rf = ParseDataFormat(hashStr, strURIPart); | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | switch (rf) { | ||||
default: { | default: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: " + | "output format not found (available: " + | ||||
AvailableDataFormatsString() + ")"); | AvailableDataFormatsString() + ")"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static bool rest_getutxos(Config &config, HTTPRequest *req, | static bool rest_getutxos(Config &config, const util::Ref &context, | ||||
const std::string &strURIPart) { | HTTPRequest *req, const std::string &strURIPart) { | ||||
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); | ||||
std::vector<std::string> uriParts; | std::vector<std::string> uriParts; | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | bitmap.resize((vOutPoints.size() + 7) / 8); | ||||
hits.push_back(hit); | hits.push_back(hit); | ||||
if (hit) { | if (hit) { | ||||
outs.emplace_back(std::move(coin)); | outs.emplace_back(std::move(coin)); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
if (fCheckMemPool) { | if (fCheckMemPool) { | ||||
const CTxMemPool *mempool = GetMemPool(req); | const CTxMemPool *mempool = GetMemPool(context, req); | ||||
if (!mempool) { | if (!mempool) { | ||||
return false; | return false; | ||||
} | } | ||||
// use db+mempool as cache backend in case user likes to query | // use db+mempool as cache backend in case user likes to query | ||||
// mempool | // mempool | ||||
LOCK2(cs_main, mempool->cs); | LOCK2(cs_main, mempool->cs); | ||||
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); | CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | switch (rf) { | ||||
default: { | default: { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
"output format not found (available: " + | "output format not found (available: " + | ||||
AvailableDataFormatsString() + ")"); | AvailableDataFormatsString() + ")"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static bool rest_blockhash_by_height(Config &config, HTTPRequest *req, | static bool rest_blockhash_by_height(Config &config, const util::Ref &context, | ||||
HTTPRequest *req, | |||||
const std::string &str_uri_part) { | const std::string &str_uri_part) { | ||||
if (!CheckWarmup(req)) { | if (!CheckWarmup(req)) { | ||||
return false; | return false; | ||||
} | } | ||||
std::string height_str; | std::string height_str; | ||||
const RetFormat rf = ParseDataFormat(height_str, str_uri_part); | const RetFormat rf = ParseDataFormat(height_str, str_uri_part); | ||||
int32_t blockheight; | int32_t blockheight; | ||||
Show All 36 Lines | switch (rf) { | ||||
"output format not found (available: " + | "output format not found (available: " + | ||||
AvailableDataFormatsString() + ")"); | AvailableDataFormatsString() + ")"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static const struct { | static const struct { | ||||
const char *prefix; | const char *prefix; | ||||
bool (*handler)(Config &config, HTTPRequest *req, | bool (*handler)(Config &config, const util::Ref &context, HTTPRequest *req, | ||||
const std::string &strReq); | const std::string &strReq); | ||||
} uri_prefixes[] = { | } uri_prefixes[] = { | ||||
{"/rest/tx/", rest_tx}, | {"/rest/tx/", rest_tx}, | ||||
{"/rest/block/notxdetails/", rest_block_notxdetails}, | {"/rest/block/notxdetails/", rest_block_notxdetails}, | ||||
{"/rest/block/", rest_block_extended}, | {"/rest/block/", rest_block_extended}, | ||||
{"/rest/chaininfo", rest_chaininfo}, | {"/rest/chaininfo", rest_chaininfo}, | ||||
{"/rest/mempool/info", rest_mempool_info}, | {"/rest/mempool/info", rest_mempool_info}, | ||||
{"/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}, | {"/rest/blockhashbyheight/", rest_blockhash_by_height}, | ||||
}; | }; | ||||
void StartREST() { | void StartREST(const util::Ref &context) { | ||||
for (size_t i = 0; i < ARRAYLEN(uri_prefixes); i++) { | for (const auto &up : uri_prefixes) { | ||||
RegisterHTTPHandler(uri_prefixes[i].prefix, false, | auto handler = [&context, up](Config &config, HTTPRequest *req, | ||||
uri_prefixes[i].handler); | const std::string &prefix) { | ||||
return up.handler(config, context, req, prefix); | |||||
}; | |||||
RegisterHTTPHandler(up.prefix, false, handler); | |||||
} | } | ||||
} | } | ||||
void InterruptREST() {} | void InterruptREST() {} | ||||
void StopREST() { | void StopREST() { | ||||
for (size_t i = 0; i < ARRAYLEN(uri_prefixes); i++) { | for (size_t i = 0; i < ARRAYLEN(uri_prefixes); i++) { | ||||
UnregisterHTTPHandler(uri_prefixes[i].prefix, false); | UnregisterHTTPHandler(uri_prefixes[i].prefix, false); | ||||
} | } | ||||
} | } |