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