Changeset View
Changeset View
Standalone View
Standalone View
src/rest.cpp
Show First 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | static CTxMemPool *GetMemPool(const std::any &context, HTTPRequest *req) { | ||||
auto node_context = util::AnyPtr<NodeContext>(context); | auto node_context = util::AnyPtr<NodeContext>(context); | ||||
if (!node_context || !node_context->mempool) { | if (!node_context || !node_context->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 node_context->mempool.get(); | return node_context->mempool.get(); | ||||
} | } | ||||
/** | |||||
* Get the node context chainstatemanager. | |||||
* | |||||
* @param[in] req The HTTP request, whose status code will be set if node | |||||
* context chainstatemanager is not found. | |||||
* @returns Pointer to the chainstatemanager or nullptr if none found. | |||||
*/ | |||||
static ChainstateManager *GetChainman(const std::any &context, | |||||
HTTPRequest *req) { | |||||
auto node_context = util::AnyPtr<NodeContext>(context); | |||||
if (!node_context || !node_context->chainman) { | |||||
RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, | |||||
strprintf("%s:%d (%s)\n" | |||||
"Internal bug detected: Chainman disabled or instance" | |||||
" not found!\n" | |||||
"You may report this issue here: %s\n", | |||||
__FILE__, __LINE__, __func__, PACKAGE_BUGREPORT)); | |||||
return nullptr; | |||||
} | |||||
return node_context->chainman; | |||||
} | |||||
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 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | static bool rest_headers(Config &config, const std::any &context, | ||||
const BlockHash hash(rawHash); | const BlockHash hash(rawHash); | ||||
const CBlockIndex *tip = nullptr; | const CBlockIndex *tip = nullptr; | ||||
std::vector<const CBlockIndex *> headers; | std::vector<const CBlockIndex *> headers; | ||||
headers.reserve(count); | headers.reserve(count); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
tip = ::ChainActive().Tip(); | ChainstateManager *maybe_chainman = GetChainman(context, req); | ||||
const CBlockIndex *pindex = | if (!maybe_chainman) { | ||||
g_chainman.m_blockman.LookupBlockIndex(hash); | return false; | ||||
while (pindex != nullptr && ::ChainActive().Contains(pindex)) { | } | ||||
ChainstateManager &chainman = *maybe_chainman; | |||||
CChain &active_chain = chainman.ActiveChain(); | |||||
tip = active_chain.Tip(); | |||||
const CBlockIndex *pindex = chainman.m_blockman.LookupBlockIndex(hash); | |||||
while (pindex != nullptr && active_chain.Contains(pindex)) { | |||||
headers.push_back(pindex); | headers.push_back(pindex); | ||||
if (headers.size() == size_t(count)) { | if (headers.size() == size_t(count)) { | ||||
break; | break; | ||||
} | } | ||||
pindex = ::ChainActive().Next(pindex); | pindex = active_chain.Next(pindex); | ||||
} | } | ||||
} | } | ||||
switch (rf) { | switch (rf) { | ||||
case RetFormat::BINARY: { | case RetFormat::BINARY: { | ||||
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(); | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | static bool rest_block(const Config &config, const std::any &context, | ||||
const BlockHash hash(rawHash); | const BlockHash hash(rawHash); | ||||
CBlock block; | CBlock block; | ||||
CBlockIndex *pblockindex = nullptr; | CBlockIndex *pblockindex = nullptr; | ||||
CBlockIndex *tip = nullptr; | CBlockIndex *tip = nullptr; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
tip = ::ChainActive().Tip(); | ChainstateManager *maybe_chainman = GetChainman(context, req); | ||||
pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); | if (!maybe_chainman) { | ||||
return false; | |||||
} | |||||
ChainstateManager &chainman = *maybe_chainman; | |||||
tip = chainman.ActiveTip(); | |||||
pblockindex = chainman.m_blockman.LookupBlockIndex(hash); | |||||
if (!pblockindex) { | if (!pblockindex) { | ||||
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); | return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); | ||||
} | } | ||||
if (IsBlockPruned(pblockindex)) { | if (IsBlockPruned(pblockindex)) { | ||||
return RESTERR(req, HTTP_NOT_FOUND, | return RESTERR(req, HTTP_NOT_FOUND, | ||||
hashStr + " not available (pruned data)"); | hashStr + " not available (pruned data)"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 326 Lines • ▼ Show 20 Lines | static bool rest_getutxos(Config &config, const std::any &context, | ||||
// check spentness and form a bitmap (as well as a JSON capable | // check spentness and form a bitmap (as well as a JSON capable | ||||
// human-readable string representation) | // human-readable string representation) | ||||
std::vector<uint8_t> bitmap; | std::vector<uint8_t> bitmap; | ||||
std::vector<CCoin> outs; | std::vector<CCoin> outs; | ||||
std::string bitmapStringRepresentation; | std::string bitmapStringRepresentation; | ||||
std::vector<bool> hits; | std::vector<bool> hits; | ||||
bitmap.resize((vOutPoints.size() + 7) / 8); | bitmap.resize((vOutPoints.size() + 7) / 8); | ||||
ChainstateManager *maybe_chainman = GetChainman(context, req); | |||||
if (!maybe_chainman) { | |||||
return false; | |||||
} | |||||
ChainstateManager &chainman = *maybe_chainman; | |||||
{ | { | ||||
auto process_utxos = [&vOutPoints, &outs, | auto process_utxos = [&vOutPoints, &outs, | ||||
&hits](const CCoinsView &view, | &hits](const CCoinsView &view, | ||||
const CTxMemPool &mempool) { | const CTxMemPool &mempool) { | ||||
for (const COutPoint &vOutPoint : vOutPoints) { | for (const COutPoint &vOutPoint : vOutPoints) { | ||||
Coin coin; | Coin coin; | ||||
bool hit = !mempool.isSpent(vOutPoint) && | bool hit = !mempool.isSpent(vOutPoint) && | ||||
view.GetCoin(vOutPoint, coin); | view.GetCoin(vOutPoint, coin); | ||||
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(context, 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 = chainman.ActiveChainstate().CoinsTip(); | ||||
CCoinsViewMemPool viewMempool(&viewChain, *mempool); | CCoinsViewMemPool viewMempool(&viewChain, *mempool); | ||||
process_utxos(viewMempool, *mempool); | process_utxos(viewMempool, *mempool); | ||||
} else { | } else { | ||||
// no need to lock mempool! | // no need to lock mempool! | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool()); | process_utxos(chainman.ActiveChainstate().CoinsTip(), CTxMemPool()); | ||||
} | } | ||||
for (size_t i = 0; i < hits.size(); ++i) { | for (size_t i = 0; i < hits.size(); ++i) { | ||||
const bool hit = hits[i]; | const bool hit = hits[i]; | ||||
// 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 RetFormat::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 << chainman.ActiveHeight() | ||||
<< ::ChainActive().Tip()->GetBlockHash() << bitmap | << chainman.ActiveTip()->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 RetFormat::HEX: { | case RetFormat::HEX: { | ||||
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); | ||||
ssGetUTXOResponse << ::ChainActive().Height() | ssGetUTXOResponse << chainman.ActiveHeight() | ||||
<< ::ChainActive().Tip()->GetBlockHash() << bitmap | << chainman.ActiveTip()->GetBlockHash() << bitmap | ||||
<< outs; | << outs; | ||||
std::string strHex = HexStr(ssGetUTXOResponse) + "\n"; | std::string strHex = HexStr(ssGetUTXOResponse) + "\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 RetFormat::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", chainman.ActiveHeight()); | ||||
objGetUTXOResponse.pushKV( | objGetUTXOResponse.pushKV( | ||||
"chaintipHash", ::ChainActive().Tip()->GetBlockHash().GetHex()); | "chaintipHash", chainman.ActiveTip()->GetBlockHash().GetHex()); | ||||
objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); | objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); | ||||
UniValue utxos(UniValue::VARR); | UniValue utxos(UniValue::VARR); | ||||
for (const CCoin &coin : outs) { | for (const CCoin &coin : outs) { | ||||
UniValue utxo(UniValue::VOBJ); | UniValue utxo(UniValue::VOBJ); | ||||
utxo.pushKV("height", int32_t(coin.nHeight)); | utxo.pushKV("height", int32_t(coin.nHeight)); | ||||
utxo.pushKV("value", coin.out.nValue); | utxo.pushKV("value", coin.out.nValue); | ||||
Show All 31 Lines | static bool rest_blockhash_by_height(Config &config, const std::any &context, | ||||
int32_t blockheight; | int32_t blockheight; | ||||
if (!ParseInt32(height_str, &blockheight) || blockheight < 0) { | if (!ParseInt32(height_str, &blockheight) || blockheight < 0) { | ||||
return RESTERR(req, HTTP_BAD_REQUEST, | return RESTERR(req, HTTP_BAD_REQUEST, | ||||
"Invalid height: " + SanitizeString(height_str)); | "Invalid height: " + SanitizeString(height_str)); | ||||
} | } | ||||
CBlockIndex *pblockindex = nullptr; | CBlockIndex *pblockindex = nullptr; | ||||
{ | { | ||||
ChainstateManager *maybe_chainman = GetChainman(context, req); | |||||
if (!maybe_chainman) { | |||||
return false; | |||||
} | |||||
ChainstateManager &chainman = *maybe_chainman; | |||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (blockheight > ::ChainActive().Height()) { | const CChain &active_chain = chainman.ActiveChain(); | ||||
if (blockheight > active_chain.Height()) { | |||||
return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range"); | return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range"); | ||||
} | } | ||||
pblockindex = ::ChainActive()[blockheight]; | pblockindex = active_chain[blockheight]; | ||||
} | } | ||||
switch (rf) { | switch (rf) { | ||||
case RetFormat::BINARY: { | case RetFormat::BINARY: { | ||||
CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION); | CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION); | ||||
ss_blockhash << pblockindex->GetBlockHash(); | ss_blockhash << pblockindex->GetBlockHash(); | ||||
req->WriteHeader("Content-Type", "application/octet-stream"); | req->WriteHeader("Content-Type", "application/octet-stream"); | ||||
req->WriteReply(HTTP_OK, ss_blockhash.str()); | req->WriteReply(HTTP_OK, ss_blockhash.str()); | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 55 Lines • Show Last 20 Lines |