Bitcoin Core 22.99.0
P2P Digital Currency
rest.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <chain.h>
7#include <chainparams.h>
8#include <core_io.h>
9#include <httpserver.h>
10#include <index/txindex.h>
11#include <node/blockstorage.h>
12#include <node/context.h>
13#include <primitives/block.h>
15#include <rpc/blockchain.h>
16#include <rpc/protocol.h>
17#include <rpc/server.h>
18#include <streams.h>
19#include <sync.h>
20#include <txmempool.h>
21#include <util/check.h>
22#include <util/system.h>
23#include <validation.h>
24#include <version.h>
25
26#include <any>
27
28#include <boost/algorithm/string.hpp>
29
30#include <univalue.h>
31
32static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
33
34enum class RetFormat {
35 UNDEF,
36 BINARY,
37 HEX,
38 JSON,
39};
40
41static const struct {
43 const char* name;
44} rf_names[] = {
45 {RetFormat::UNDEF, ""},
46 {RetFormat::BINARY, "bin"},
47 {RetFormat::HEX, "hex"},
48 {RetFormat::JSON, "json"},
49};
50
51struct CCoin {
52 uint32_t nHeight;
54
55 CCoin() : nHeight(0) {}
56 explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
57
59 {
60 uint32_t nTxVerDummy = 0;
61 READWRITE(nTxVerDummy, obj.nHeight, obj.out);
62 }
63};
64
65static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
66{
67 req->WriteHeader("Content-Type", "text/plain");
68 req->WriteReply(status, message + "\r\n");
69 return false;
70}
71
79static NodeContext* GetNodeContext(const std::any& context, HTTPRequest* req)
80{
81 auto node_context = util::AnyPtr<NodeContext>(context);
82 if (!node_context) {
84 strprintf("%s:%d (%s)\n"
85 "Internal bug detected: Node context not found!\n"
86 "You may report this issue here: %s\n",
87 __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
88 return nullptr;
89 }
90 return node_context;
91}
92
100static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req)
101{
102 auto node_context = util::AnyPtr<NodeContext>(context);
103 if (!node_context || !node_context->mempool) {
104 RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
105 return nullptr;
106 }
107 return node_context->mempool.get();
108}
109
117static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
118{
119 auto node_context = util::AnyPtr<NodeContext>(context);
120 if (!node_context || !node_context->chainman) {
122 strprintf("%s:%d (%s)\n"
123 "Internal bug detected: Chainman disabled or instance not found!\n"
124 "You may report this issue here: %s\n",
125 __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
126 return nullptr;
127 }
128 return node_context->chainman.get();
129}
130
131static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
132{
133 const std::string::size_type pos = strReq.rfind('.');
134 if (pos == std::string::npos)
135 {
136 param = strReq;
137 return rf_names[0].rf;
138 }
139
140 param = strReq.substr(0, pos);
141 const std::string suff(strReq, pos + 1);
142
143 for (const auto& rf_name : rf_names) {
144 if (suff == rf_name.name)
145 return rf_name.rf;
146 }
147
148 /* If no suffix is found, return original string. */
149 param = strReq;
150 return rf_names[0].rf;
151}
152
153static std::string AvailableDataFormatsString()
154{
155 std::string formats;
156 for (const auto& rf_name : rf_names) {
157 if (strlen(rf_name.name) > 0) {
158 formats.append(".");
159 formats.append(rf_name.name);
160 formats.append(", ");
161 }
162 }
163
164 if (formats.length() > 0)
165 return formats.substr(0, formats.length() - 2);
166
167 return formats;
168}
169
170static bool CheckWarmup(HTTPRequest* req)
171{
172 std::string statusmessage;
173 if (RPCIsInWarmup(&statusmessage))
174 return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
175 return true;
176}
177
178static bool rest_headers(const std::any& context,
179 HTTPRequest* req,
180 const std::string& strURIPart)
181{
182 if (!CheckWarmup(req))
183 return false;
184 std::string param;
185 const RetFormat rf = ParseDataFormat(param, strURIPart);
186 std::vector<std::string> path;
187 boost::split(path, param, boost::is_any_of("/"));
188
189 if (path.size() != 2)
190 return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
191
192 const auto parsed_count{ToIntegral<size_t>(path[0])};
193 if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > 2000) {
194 return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
195 }
196
197 std::string hashStr = path[1];
198 uint256 hash;
199 if (!ParseHashStr(hashStr, hash))
200 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
201
202 const CBlockIndex* tip = nullptr;
203 std::vector<const CBlockIndex*> headers;
204 headers.reserve(*parsed_count);
205 {
206 ChainstateManager* maybe_chainman = GetChainman(context, req);
207 if (!maybe_chainman) return false;
208 ChainstateManager& chainman = *maybe_chainman;
209 LOCK(cs_main);
210 CChain& active_chain = chainman.ActiveChain();
211 tip = active_chain.Tip();
212 const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
213 while (pindex != nullptr && active_chain.Contains(pindex)) {
214 headers.push_back(pindex);
215 if (headers.size() == *parsed_count) {
216 break;
217 }
218 pindex = active_chain.Next(pindex);
219 }
220 }
221
222 switch (rf) {
223 case RetFormat::BINARY: {
225 for (const CBlockIndex *pindex : headers) {
226 ssHeader << pindex->GetBlockHeader();
227 }
228
229 std::string binaryHeader = ssHeader.str();
230 req->WriteHeader("Content-Type", "application/octet-stream");
231 req->WriteReply(HTTP_OK, binaryHeader);
232 return true;
233 }
234
235 case RetFormat::HEX: {
237 for (const CBlockIndex *pindex : headers) {
238 ssHeader << pindex->GetBlockHeader();
239 }
240
241 std::string strHex = HexStr(ssHeader) + "\n";
242 req->WriteHeader("Content-Type", "text/plain");
243 req->WriteReply(HTTP_OK, strHex);
244 return true;
245 }
246 case RetFormat::JSON: {
247 UniValue jsonHeaders(UniValue::VARR);
248 for (const CBlockIndex *pindex : headers) {
249 jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
250 }
251 std::string strJSON = jsonHeaders.write() + "\n";
252 req->WriteHeader("Content-Type", "application/json");
253 req->WriteReply(HTTP_OK, strJSON);
254 return true;
255 }
256 default: {
257 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex, .json)");
258 }
259 }
260}
261
262static bool rest_block(const std::any& context,
263 HTTPRequest* req,
264 const std::string& strURIPart,
265 TxVerbosity tx_verbosity)
266{
267 if (!CheckWarmup(req))
268 return false;
269 std::string hashStr;
270 const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
271
272 uint256 hash;
273 if (!ParseHashStr(hashStr, hash))
274 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
275
276 CBlock block;
277 CBlockIndex* pblockindex = nullptr;
278 CBlockIndex* tip = nullptr;
279 {
280 ChainstateManager* maybe_chainman = GetChainman(context, req);
281 if (!maybe_chainman) return false;
282 ChainstateManager& chainman = *maybe_chainman;
283 LOCK(cs_main);
284 tip = chainman.ActiveChain().Tip();
285 pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
286 if (!pblockindex) {
287 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
288 }
289
290 if (IsBlockPruned(pblockindex))
291 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
292
293 if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
294 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
295 }
296
297 switch (rf) {
298 case RetFormat::BINARY: {
300 ssBlock << block;
301 std::string binaryBlock = ssBlock.str();
302 req->WriteHeader("Content-Type", "application/octet-stream");
303 req->WriteReply(HTTP_OK, binaryBlock);
304 return true;
305 }
306
307 case RetFormat::HEX: {
309 ssBlock << block;
310 std::string strHex = HexStr(ssBlock) + "\n";
311 req->WriteHeader("Content-Type", "text/plain");
312 req->WriteReply(HTTP_OK, strHex);
313 return true;
314 }
315
316 case RetFormat::JSON: {
317 UniValue objBlock = blockToJSON(block, tip, pblockindex, tx_verbosity);
318 std::string strJSON = objBlock.write() + "\n";
319 req->WriteHeader("Content-Type", "application/json");
320 req->WriteReply(HTTP_OK, strJSON);
321 return true;
322 }
323
324 default: {
325 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
326 }
327 }
328}
329
330static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
331{
332 return rest_block(context, req, strURIPart, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
333}
334
335static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
336{
337 return rest_block(context, req, strURIPart, TxVerbosity::SHOW_TXID);
338}
339
340// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
342
343static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
344{
345 if (!CheckWarmup(req))
346 return false;
347 std::string param;
348 const RetFormat rf = ParseDataFormat(param, strURIPart);
349
350 switch (rf) {
351 case RetFormat::JSON: {
352 JSONRPCRequest jsonRequest;
353 jsonRequest.context = context;
354 jsonRequest.params = UniValue(UniValue::VARR);
355 UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
356 std::string strJSON = chainInfoObject.write() + "\n";
357 req->WriteHeader("Content-Type", "application/json");
358 req->WriteReply(HTTP_OK, strJSON);
359 return true;
360 }
361 default: {
362 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
363 }
364 }
365}
366
367static bool rest_mempool_info(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
368{
369 if (!CheckWarmup(req))
370 return false;
371 const CTxMemPool* mempool = GetMemPool(context, req);
372 if (!mempool) return false;
373 std::string param;
374 const RetFormat rf = ParseDataFormat(param, strURIPart);
375
376 switch (rf) {
377 case RetFormat::JSON: {
378 UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
379
380 std::string strJSON = mempoolInfoObject.write() + "\n";
381 req->WriteHeader("Content-Type", "application/json");
382 req->WriteReply(HTTP_OK, strJSON);
383 return true;
384 }
385 default: {
386 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
387 }
388 }
389}
390
391static bool rest_mempool_contents(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
392{
393 if (!CheckWarmup(req)) return false;
394 const CTxMemPool* mempool = GetMemPool(context, req);
395 if (!mempool) return false;
396 std::string param;
397 const RetFormat rf = ParseDataFormat(param, strURIPart);
398
399 switch (rf) {
400 case RetFormat::JSON: {
401 UniValue mempoolObject = MempoolToJSON(*mempool, true);
402
403 std::string strJSON = mempoolObject.write() + "\n";
404 req->WriteHeader("Content-Type", "application/json");
405 req->WriteReply(HTTP_OK, strJSON);
406 return true;
407 }
408 default: {
409 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
410 }
411 }
412}
413
414static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
415{
416 if (!CheckWarmup(req))
417 return false;
418 std::string hashStr;
419 const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
420
421 uint256 hash;
422 if (!ParseHashStr(hashStr, hash))
423 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
424
425 if (g_txindex) {
426 g_txindex->BlockUntilSyncedToCurrentChain();
427 }
428
429 const NodeContext* const node = GetNodeContext(context, req);
430 if (!node) return false;
431 uint256 hashBlock = uint256();
432 const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool.get(), hash, Params().GetConsensus(), hashBlock);
433 if (!tx) {
434 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
435 }
436
437 switch (rf) {
438 case RetFormat::BINARY: {
440 ssTx << tx;
441
442 std::string binaryTx = ssTx.str();
443 req->WriteHeader("Content-Type", "application/octet-stream");
444 req->WriteReply(HTTP_OK, binaryTx);
445 return true;
446 }
447
448 case RetFormat::HEX: {
450 ssTx << tx;
451
452 std::string strHex = HexStr(ssTx) + "\n";
453 req->WriteHeader("Content-Type", "text/plain");
454 req->WriteReply(HTTP_OK, strHex);
455 return true;
456 }
457
458 case RetFormat::JSON: {
460 TxToUniv(*tx, hashBlock, objTx);
461 std::string strJSON = objTx.write() + "\n";
462 req->WriteHeader("Content-Type", "application/json");
463 req->WriteReply(HTTP_OK, strJSON);
464 return true;
465 }
466
467 default: {
468 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
469 }
470 }
471}
472
473static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
474{
475 if (!CheckWarmup(req))
476 return false;
477 std::string param;
478 const RetFormat rf = ParseDataFormat(param, strURIPart);
479
480 std::vector<std::string> uriParts;
481 if (param.length() > 1)
482 {
483 std::string strUriParams = param.substr(1);
484 boost::split(uriParts, strUriParams, boost::is_any_of("/"));
485 }
486
487 // throw exception in case of an empty request
488 std::string strRequestMutable = req->ReadBody();
489 if (strRequestMutable.length() == 0 && uriParts.size() == 0)
490 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
491
492 bool fInputParsed = false;
493 bool fCheckMemPool = false;
494 std::vector<COutPoint> vOutPoints;
495
496 // parse/deserialize input
497 // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
498
499 if (uriParts.size() > 0)
500 {
501 //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
502 if (uriParts[0] == "checkmempool") fCheckMemPool = true;
503
504 for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
505 {
506 uint256 txid;
507 int32_t nOutput;
508 std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
509 std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1);
510
511 if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
512 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
513
514 txid.SetHex(strTxid);
515 vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
516 }
517
518 if (vOutPoints.size() > 0)
519 fInputParsed = true;
520 else
521 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
522 }
523
524 switch (rf) {
525 case RetFormat::HEX: {
526 // convert hex to bin, continue then with bin part
527 std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
528 strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
529 [[fallthrough]];
530 }
531
532 case RetFormat::BINARY: {
533 try {
534 //deserialize only if user sent a request
535 if (strRequestMutable.size() > 0)
536 {
537 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
538 return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
539
541 oss << strRequestMutable;
542 oss >> fCheckMemPool;
543 oss >> vOutPoints;
544 }
545 } catch (const std::ios_base::failure&) {
546 // abort in case of unreadable binary data
547 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
548 }
549 break;
550 }
551
552 case RetFormat::JSON: {
553 if (!fInputParsed)
554 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
555 break;
556 }
557 default: {
558 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
559 }
560 }
561
562 // limit max outpoints
563 if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
564 return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
565
566 // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
567 std::vector<unsigned char> bitmap;
568 std::vector<CCoin> outs;
569 std::string bitmapStringRepresentation;
570 std::vector<bool> hits;
571 bitmap.resize((vOutPoints.size() + 7) / 8);
572 ChainstateManager* maybe_chainman = GetChainman(context, req);
573 if (!maybe_chainman) return false;
574 ChainstateManager& chainman = *maybe_chainman;
575 {
576 auto process_utxos = [&vOutPoints, &outs, &hits](const CCoinsView& view, const CTxMemPool& mempool) {
577 for (const COutPoint& vOutPoint : vOutPoints) {
578 Coin coin;
579 bool hit = !mempool.isSpent(vOutPoint) && view.GetCoin(vOutPoint, coin);
580 hits.push_back(hit);
581 if (hit) outs.emplace_back(std::move(coin));
582 }
583 };
584
585 if (fCheckMemPool) {
586 const CTxMemPool* mempool = GetMemPool(context, req);
587 if (!mempool) return false;
588 // use db+mempool as cache backend in case user likes to query mempool
589 LOCK2(cs_main, mempool->cs);
590 CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip();
591 CCoinsViewMemPool viewMempool(&viewChain, *mempool);
592 process_utxos(viewMempool, *mempool);
593 } else {
594 LOCK(cs_main); // no need to lock mempool!
595 process_utxos(chainman.ActiveChainstate().CoinsTip(), CTxMemPool());
596 }
597
598 for (size_t i = 0; i < hits.size(); ++i) {
599 const bool hit = hits[i];
600 bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
601 bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
602 }
603 }
604
605 switch (rf) {
606 case RetFormat::BINARY: {
607 // serialize data
608 // use exact same output as mentioned in Bip64
609 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
610 ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
611 std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
612
613 req->WriteHeader("Content-Type", "application/octet-stream");
614 req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
615 return true;
616 }
617
618 case RetFormat::HEX: {
619 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
620 ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs;
621 std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
622
623 req->WriteHeader("Content-Type", "text/plain");
624 req->WriteReply(HTTP_OK, strHex);
625 return true;
626 }
627
628 case RetFormat::JSON: {
629 UniValue objGetUTXOResponse(UniValue::VOBJ);
630
631 // pack in some essentials
632 // use more or less the same output as mentioned in Bip64
633 objGetUTXOResponse.pushKV("chainHeight", chainman.ActiveChain().Height());
634 objGetUTXOResponse.pushKV("chaintipHash", chainman.ActiveChain().Tip()->GetBlockHash().GetHex());
635 objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
636
638 for (const CCoin& coin : outs) {
640 utxo.pushKV("height", (int32_t)coin.nHeight);
641 utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
642
643 // include the script in a json output
645 ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
646 utxo.pushKV("scriptPubKey", o);
647 utxos.push_back(utxo);
648 }
649 objGetUTXOResponse.pushKV("utxos", utxos);
650
651 // return json string
652 std::string strJSON = objGetUTXOResponse.write() + "\n";
653 req->WriteHeader("Content-Type", "application/json");
654 req->WriteReply(HTTP_OK, strJSON);
655 return true;
656 }
657 default: {
658 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
659 }
660 }
661}
662
663static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
664 const std::string& str_uri_part)
665{
666 if (!CheckWarmup(req)) return false;
667 std::string height_str;
668 const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
669
670 int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
671 if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
672 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
673 }
674
675 CBlockIndex* pblockindex = nullptr;
676 {
677 ChainstateManager* maybe_chainman = GetChainman(context, req);
678 if (!maybe_chainman) return false;
679 ChainstateManager& chainman = *maybe_chainman;
680 LOCK(cs_main);
681 const CChain& active_chain = chainman.ActiveChain();
682 if (blockheight > active_chain.Height()) {
683 return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
684 }
685 pblockindex = active_chain[blockheight];
686 }
687 switch (rf) {
688 case RetFormat::BINARY: {
690 ss_blockhash << pblockindex->GetBlockHash();
691 req->WriteHeader("Content-Type", "application/octet-stream");
692 req->WriteReply(HTTP_OK, ss_blockhash.str());
693 return true;
694 }
695 case RetFormat::HEX: {
696 req->WriteHeader("Content-Type", "text/plain");
697 req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
698 return true;
699 }
700 case RetFormat::JSON: {
701 req->WriteHeader("Content-Type", "application/json");
703 resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
704 req->WriteReply(HTTP_OK, resp.write() + "\n");
705 return true;
706 }
707 default: {
708 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
709 }
710 }
711}
712
713static const struct {
714 const char* prefix;
715 bool (*handler)(const std::any& context, HTTPRequest* req, const std::string& strReq);
716} uri_prefixes[] = {
717 {"/rest/tx/", rest_tx},
718 {"/rest/block/notxdetails/", rest_block_notxdetails},
719 {"/rest/block/", rest_block_extended},
720 {"/rest/chaininfo", rest_chaininfo},
721 {"/rest/mempool/info", rest_mempool_info},
722 {"/rest/mempool/contents", rest_mempool_contents},
723 {"/rest/headers/", rest_headers},
724 {"/rest/getutxos", rest_getutxos},
725 {"/rest/blockhashbyheight/", rest_blockhash_by_height},
727
728void StartREST(const std::any& context)
729{
730 for (const auto& up : uri_prefixes) {
731 auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
732 RegisterHTTPHandler(up.prefix, false, handler);
733 }
734}
735
737{
738}
739
741{
742 for (const auto& up : uri_prefixes) {
743 UnregisterHTTPHandler(up.prefix, false);
744 }
745}
#define PACKAGE_BUGREPORT
UniValue MempoolInfoToJSON(const CTxMemPool &pool)
Mempool information to JSON.
UniValue blockToJSON(const CBlock &block, const CBlockIndex *tip, const CBlockIndex *blockindex, TxVerbosity verbosity)
Block description to JSON.
Definition: blockchain.cpp:203
UniValue MempoolToJSON(const CTxMemPool &pool, bool verbose, bool include_mempool_sequence)
Mempool to JSON.
Definition: blockchain.cpp:553
UniValue blockheaderToJSON(const CBlockIndex *tip, const CBlockIndex *blockindex)
Block header to JSON.
Definition: blockchain.cpp:174
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:118
bool IsBlockPruned(const CBlockIndex *pblockindex)
Check whether the block associated with this index entry is pruned or not.
bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos, const Consensus::Params &consensusParams)
Functions for disk access for blocks.
const CChainParams & Params()
Return the currently selected parameters.
Definition: block.h:63
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:146
uint256 GetBlockHash() const
Definition: chain.h:254
An in-memory indexed chain of blocks.
Definition: chain.h:410
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:421
CBlockIndex * Next(const CBlockIndex *pindex) const
Find the successor of a block in this chain, or nullptr if the given index is not found or is the tip...
Definition: chain.h:438
int Height() const
Return the maximal height in the chain.
Definition: chain.h:446
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:433
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: validation.h:638
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:214
Abstract view on the open txout dataset.
Definition: coins.h:158
CCoinsView that brings transactions from a mempool into view.
Definition: txmempool.h:852
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
std::string str() const
Definition: streams.h:242
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:27
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:424
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:511
An output of a transaction.
Definition: transaction.h:129
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:847
CChainState & ActiveChainstate() const
The most-work chain.
CChain & ActiveChain() const
Definition: validation.h:945
A UTXO entry.
Definition: coins.h:31
In-flight HTTP request.
Definition: httpserver.h:57
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:562
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:550
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:530
UniValue params
Definition: request.h:33
std::any context
Definition: request.h:38
UniValue HandleRequest(const JSONRPCRequest &request) const
Definition: util.cpp:564
@ VOBJ
Definition: univalue.h:19
@ VARR
Definition: univalue.h:19
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
bool pushKV(const std::string &key, const UniValue &val)
Definition: univalue.cpp:133
void SetHex(const char *psz)
Definition: uint256.cpp:30
std::string GetHex() const
Definition: uint256.cpp:20
256-bit opaque blob.
Definition: uint256.h:124
bool ParseHashStr(const std::string &strHex, uint256 &result)
Parse a hex string into 256 bits.
Definition: core_read.cpp:230
TxVerbosity
Verbose level for block's transaction.
Definition: core_io.h:26
@ SHOW_DETAILS_AND_PREVOUT
The same as previous option with information about prevouts if available.
@ SHOW_TXID
Only TXID for each block's transaction.
void ScriptPubKeyToUniv(const CScript &scriptPubKey, UniValue &out, bool include_hex, bool include_address=true)
Definition: core_write.cpp:150
void TxToUniv(const CTransaction &tx, const uint256 &hashBlock, UniValue &entry, bool include_hex=true, int serialize_flags=0, const CTxUndo *txundo=nullptr, TxVerbosity verbosity=TxVerbosity::SHOW_DETAILS)
Definition: core_write.cpp:166
UniValue ValueFromAmount(const CAmount amount)
Definition: core_write.cpp:21
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:638
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:632
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const uint256 &hash, const Consensus::Params &consensusParams, uint256 &hashBlock)
Return transaction with a given hash.
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:386
static bool rest_blockhash_by_height(const std::any &context, HTTPRequest *req, const std::string &str_uri_part)
Definition: rest.cpp:663
static bool rest_getutxos(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:473
static bool rest_headers(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:178
static RetFormat ParseDataFormat(std::string &param, const std::string &strReq)
Definition: rest.cpp:131
static bool rest_tx(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:414
const char * prefix
Definition: rest.cpp:714
void StartREST(const std::any &context)
Start HTTP REST subsystem.
Definition: rest.cpp:728
static bool rest_mempool_contents(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:391
static bool rest_block_notxdetails(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:335
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:715
static const struct @10 uri_prefixes[]
void StopREST()
Stop HTTP REST subsystem.
Definition: rest.cpp:740
const char * name
Definition: rest.cpp:43
RPCHelpMan getblockchaininfo()
static bool rest_chaininfo(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:343
static bool rest_mempool_info(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:367
RetFormat rf
Definition: rest.cpp:42
RetFormat
Definition: rest.cpp:34
void InterruptREST()
Interrupt RPC REST subsystem.
Definition: rest.cpp:736
static bool RESTERR(HTTPRequest *req, enum HTTPStatusCode status, std::string message)
Definition: rest.cpp:65
static const struct @9 rf_names[]
static bool CheckWarmup(HTTPRequest *req)
Definition: rest.cpp:170
static ChainstateManager * GetChainman(const std::any &context, HTTPRequest *req)
Get the node context chainstatemanager.
Definition: rest.cpp:117
static bool rest_block_extended(const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:330
static CTxMemPool * GetMemPool(const std::any &context, HTTPRequest *req)
Get the node context mempool.
Definition: rest.cpp:100
static bool rest_block(const std::any &context, HTTPRequest *req, const std::string &strURIPart, TxVerbosity tx_verbosity)
Definition: rest.cpp:262
static const size_t MAX_GETUTXOS_OUTPOINTS
Definition: rest.cpp:32
static std::string AvailableDataFormatsString()
Definition: rest.cpp:153
static NodeContext * GetNodeContext(const std::any &context, HTTPRequest *req)
Get the node context.
Definition: rest.cpp:79
HTTPStatusCode
HTTP status codes.
Definition: protocol.h:11
@ HTTP_BAD_REQUEST
Definition: protocol.h:13
@ HTTP_OK
Definition: protocol.h:12
@ HTTP_SERVICE_UNAVAILABLE
Definition: protocol.h:19
@ HTTP_NOT_FOUND
Definition: protocol.h:16
@ HTTP_INTERNAL_SERVER_ERROR
Definition: protocol.h:18
@ SER_NETWORK
Definition: serialize.h:138
#define READWRITE(...)
Definition: serialize.h:147
bool RPCIsInWarmup(std::string *outStatus)
Definition: server.cpp:344
int RPCSerializationFlags()
Definition: server.cpp:540
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< unsigned char > ParseHex(const char *psz)
bool ParseInt32(const std::string &str, int32_t *out)
Convert string to signed 32-bit integer with strict parse error feedback.
bool IsHex(const std::string &str)
std::string SanitizeString(const std::string &str, int rule)
Remove unsafe chars.
Definition: rest.cpp:51
CTxOut out
Definition: rest.cpp:53
CCoin(Coin &&in)
Definition: rest.cpp:56
uint32_t nHeight
Definition: rest.cpp:52
CCoin()
Definition: rest.cpp:55
SERIALIZE_METHODS(CCoin, obj)
Definition: rest.cpp:58
NodeContext struct containing references to chain state and connection state.
Definition: context.h:39
#define LOCK2(cs1, cs2)
Definition: sync.h:227
#define LOCK(cs)
Definition: sync.h:226
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:14
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12