Bitcoin ABC 0.30.7
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;
58
59 CCoin() : nHeight(0) {}
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 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: {
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: {
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 bool showTxDetails) {
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
310 if (chainman.m_blockman.IsBlockPruned(pblockindex)) {
311 return RESTERR(req, HTTP_NOT_FOUND,
312 hashStr + " not available (pruned data)");
313 }
314 }
315 if (!chainman.m_blockman.ReadBlockFromDisk(block, *pblockindex)) {
316 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
317 }
318
319 switch (rf) {
320 case RetFormat::BINARY: {
321 CDataStream ssBlock(SER_NETWORK,
323 ssBlock << block;
324 std::string binaryBlock = ssBlock.str();
325 req->WriteHeader("Content-Type", "application/octet-stream");
326 req->WriteReply(HTTP_OK, binaryBlock);
327 return true;
328 }
329
330 case RetFormat::HEX: {
331 CDataStream ssBlock(SER_NETWORK,
333 ssBlock << block;
334 std::string strHex = HexStr(ssBlock) + "\n";
335 req->WriteHeader("Content-Type", "text/plain");
336 req->WriteReply(HTTP_OK, strHex);
337 return true;
338 }
339
340 case RetFormat::JSON: {
341 UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip,
342 pblockindex, showTxDetails);
343 std::string strJSON = objBlock.write() + "\n";
344 req->WriteHeader("Content-Type", "application/json");
345 req->WriteReply(HTTP_OK, strJSON);
346 return true;
347 }
348
349 default: {
350 return RESTERR(req, HTTP_NOT_FOUND,
351 "output format not found (available: " +
353 }
354 }
355}
356
357static bool rest_block_extended(Config &config, const std::any &context,
358 HTTPRequest *req,
359 const std::string &strURIPart) {
360 return rest_block(config, context, req, strURIPart, true);
361}
362
363static bool rest_block_notxdetails(Config &config, const std::any &context,
364 HTTPRequest *req,
365 const std::string &strURIPart) {
366 return rest_block(config, context, req, strURIPart, false);
367}
368
369static bool rest_chaininfo(Config &config, const std::any &context,
370 HTTPRequest *req, const std::string &strURIPart) {
371 if (!CheckWarmup(req)) {
372 return false;
373 }
374
375 std::string param;
376 const RetFormat rf = ParseDataFormat(param, strURIPart);
377
378 switch (rf) {
379 case RetFormat::JSON: {
380 JSONRPCRequest jsonRequest;
381 jsonRequest.context = context;
382 jsonRequest.params = UniValue(UniValue::VARR);
383 UniValue chainInfoObject =
384 getblockchaininfo().HandleRequest(config, jsonRequest);
385 std::string strJSON = chainInfoObject.write() + "\n";
386 req->WriteHeader("Content-Type", "application/json");
387 req->WriteReply(HTTP_OK, strJSON);
388 return true;
389 }
390 default: {
391 return RESTERR(req, HTTP_NOT_FOUND,
392 "output format not found (available: json)");
393 }
394 }
395}
396
397static bool rest_mempool_info(Config &config, const std::any &context,
398 HTTPRequest *req, const std::string &strURIPart) {
399 if (!CheckWarmup(req)) {
400 return false;
401 }
402
403 const CTxMemPool *mempool = GetMemPool(context, req);
404 if (!mempool) {
405 return false;
406 }
407
408 std::string param;
409 const RetFormat rf = ParseDataFormat(param, strURIPart);
410
411 switch (rf) {
412 case RetFormat::JSON: {
413 UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
414
415 std::string strJSON = mempoolInfoObject.write() + "\n";
416 req->WriteHeader("Content-Type", "application/json");
417 req->WriteReply(HTTP_OK, strJSON);
418 return true;
419 }
420 default: {
421 return RESTERR(req, HTTP_NOT_FOUND,
422 "output format not found (available: json)");
423 }
424 }
425}
426
427static bool rest_mempool_contents(Config &config, const std::any &context,
428 HTTPRequest *req,
429 const std::string &strURIPart) {
430 if (!CheckWarmup(req)) {
431 return false;
432 }
433
434 const CTxMemPool *mempool = GetMemPool(context, req);
435 if (!mempool) {
436 return false;
437 }
438
439 std::string param;
440 const RetFormat rf = ParseDataFormat(param, strURIPart);
441
442 switch (rf) {
443 case RetFormat::JSON: {
444 UniValue mempoolObject = MempoolToJSON(*mempool, true);
445
446 std::string strJSON = mempoolObject.write() + "\n";
447 req->WriteHeader("Content-Type", "application/json");
448 req->WriteReply(HTTP_OK, strJSON);
449 return true;
450 }
451 default: {
452 return RESTERR(req, HTTP_NOT_FOUND,
453 "output format not found (available: json)");
454 }
455 }
456}
457
458static bool rest_tx(Config &config, const std::any &context, HTTPRequest *req,
459 const std::string &strURIPart) {
460 if (!CheckWarmup(req)) {
461 return false;
462 }
463
464 std::string hashStr;
465 const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
466
467 uint256 hash;
468 if (!ParseHashStr(hashStr, hash)) {
469 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
470 }
471
472 const TxId txid(hash);
473
474 if (g_txindex) {
475 g_txindex->BlockUntilSyncedToCurrentChain();
476 }
477
478 const NodeContext *const node = GetNodeContext(context, req);
479 if (!node) {
480 return false;
481 }
482 BlockHash hashBlock;
483 const CTransactionRef tx =
484 GetTransaction(/* block_index */ nullptr, node->mempool.get(), txid,
485 hashBlock, node->chainman->m_blockman);
486 if (!tx) {
487 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
488 }
489
490 switch (rf) {
491 case RetFormat::BINARY: {
494 ssTx << tx;
495
496 std::string binaryTx = ssTx.str();
497 req->WriteHeader("Content-Type", "application/octet-stream");
498 req->WriteReply(HTTP_OK, binaryTx);
499 return true;
500 }
501
502 case RetFormat::HEX: {
505 ssTx << tx;
506
507 std::string strHex = HexStr(ssTx) + "\n";
508 req->WriteHeader("Content-Type", "text/plain");
509 req->WriteReply(HTTP_OK, strHex);
510 return true;
511 }
512
513 case RetFormat::JSON: {
515 TxToUniv(*tx, hashBlock, objTx);
516 std::string strJSON = objTx.write() + "\n";
517 req->WriteHeader("Content-Type", "application/json");
518 req->WriteReply(HTTP_OK, strJSON);
519 return true;
520 }
521
522 default: {
523 return RESTERR(req, HTTP_NOT_FOUND,
524 "output format not found (available: " +
526 }
527 }
528}
529
530static bool rest_getutxos(Config &config, const std::any &context,
531 HTTPRequest *req, const std::string &strURIPart) {
532 if (!CheckWarmup(req)) {
533 return false;
534 }
535
536 std::string param;
537 const RetFormat rf = ParseDataFormat(param, strURIPart);
538
539 std::vector<std::string> uriParts;
540 if (param.length() > 1) {
541 std::string strUriParams = param.substr(1);
542 uriParts = SplitString(strUriParams, '/');
543 }
544
545 // throw exception in case of an empty request
546 std::string strRequestMutable = req->ReadBody();
547 if (strRequestMutable.length() == 0 && uriParts.size() == 0) {
548 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
549 }
550
551 bool fInputParsed = false;
552 bool fCheckMemPool = false;
553 std::vector<COutPoint> vOutPoints;
554
555 // parse/deserialize input
556 // input-format = output-format, rest/getutxos/bin requires binary input,
557 // gives binary output, ...
558
559 if (uriParts.size() > 0) {
560 // inputs is sent over URI scheme
561 // (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
562 if (uriParts[0] == "checkmempool") {
563 fCheckMemPool = true;
564 }
565
566 for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) {
567 int32_t nOutput;
568 std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
569 std::string strOutput =
570 uriParts[i].substr(uriParts[i].find('-') + 1);
571
572 if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) {
573 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
574 }
575
576 TxId txid;
577 txid.SetHex(strTxid);
578 vOutPoints.push_back(COutPoint(txid, uint32_t(nOutput)));
579 }
580
581 if (vOutPoints.size() > 0) {
582 fInputParsed = true;
583 } else {
584 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
585 }
586 }
587
588 switch (rf) {
589 case RetFormat::HEX: {
590 // convert hex to bin, continue then with bin part
591 std::vector<uint8_t> strRequestV = ParseHex(strRequestMutable);
592 strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
593 }
594 // FALLTHROUGH
595 case RetFormat::BINARY: {
596 try {
597 // deserialize only if user sent a request
598 if (strRequestMutable.size() > 0) {
599 // don't allow sending input over URI and HTTP RAW DATA
600 if (fInputParsed) {
601 return RESTERR(req, HTTP_BAD_REQUEST,
602 "Combination of URI scheme inputs and "
603 "raw post data is not allowed");
604 }
605
607 oss << strRequestMutable;
608 oss >> fCheckMemPool;
609 oss >> vOutPoints;
610 }
611 } catch (const std::ios_base::failure &) {
612 // abort in case of unreadable binary data
613 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
614 }
615 break;
616 }
617
618 case RetFormat::JSON: {
619 if (!fInputParsed) {
620 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
621 }
622 break;
623 }
624 default: {
625 return RESTERR(req, HTTP_NOT_FOUND,
626 "output format not found (available: " +
628 }
629 }
630
631 // limit max outpoints
632 if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) {
633 return RESTERR(
634 req, HTTP_BAD_REQUEST,
635 strprintf("Error: max outpoints exceeded (max: %d, tried: %d)",
636 MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
637 }
638
639 // check spentness and form a bitmap (as well as a JSON capable
640 // human-readable string representation)
641 std::vector<uint8_t> bitmap;
642 std::vector<CCoin> outs;
643 std::string bitmapStringRepresentation;
644 std::vector<bool> hits;
645 bitmap.resize((vOutPoints.size() + 7) / 8);
646 ChainstateManager *maybe_chainman = GetChainman(context, req);
647 if (!maybe_chainman) {
648 return false;
649 }
650 ChainstateManager &chainman = *maybe_chainman;
651 decltype(chainman.ActiveHeight()) active_height;
652 BlockHash active_hash;
653 {
654 auto process_utxos =
655 [&vOutPoints, &outs, &hits, &active_height, &active_hash,
656 &chainman](const CCoinsView &view, const CTxMemPool *mempool)
658 for (const COutPoint &vOutPoint : vOutPoints) {
659 Coin coin;
660 bool hit = (!mempool || !mempool->isSpent(vOutPoint)) &&
661 view.GetCoin(vOutPoint, coin);
662 hits.push_back(hit);
663 if (hit) {
664 outs.emplace_back(std::move(coin));
665 }
666 }
667 active_height = chainman.ActiveHeight();
668 active_hash = chainman.ActiveTip()->GetBlockHash();
669 };
670
671 if (fCheckMemPool) {
672 const CTxMemPool *mempool = GetMemPool(context, req);
673 if (!mempool) {
674 return false;
675 }
676
677 // use db+mempool as cache backend in case user likes to query
678 // mempool
679 LOCK2(cs_main, mempool->cs);
680 CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
681 CCoinsViewMemPool viewMempool(&viewChain, *mempool);
682 process_utxos(viewMempool, mempool);
683 } else {
684 // no need to lock mempool!
685 LOCK(cs_main);
686 process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr);
687 }
688
689 for (size_t i = 0; i < hits.size(); ++i) {
690 const bool hit = hits[i];
691 // form a binary string representation (human-readable for json
692 // output)
693 bitmapStringRepresentation.append(hit ? "1" : "0");
694 bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
695 }
696 }
697
698 switch (rf) {
699 case RetFormat::BINARY: {
700 // serialize data
701 // use exact same output as mentioned in Bip64
702 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
703 ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
704 std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
705
706 req->WriteHeader("Content-Type", "application/octet-stream");
707 req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
708 return true;
709 }
710
711 case RetFormat::HEX: {
712 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
713 ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
714 std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
715
716 req->WriteHeader("Content-Type", "text/plain");
717 req->WriteReply(HTTP_OK, strHex);
718 return true;
719 }
720
721 case RetFormat::JSON: {
722 UniValue objGetUTXOResponse(UniValue::VOBJ);
723
724 // pack in some essentials
725 // use more or less the same output as mentioned in Bip64
726 objGetUTXOResponse.pushKV("chainHeight", active_height);
727 objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex());
728 objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
729
731 for (const CCoin &coin : outs) {
733 utxo.pushKV("height", int32_t(coin.nHeight));
734 utxo.pushKV("value", coin.out.nValue);
735
736 // include the script in a json output
738 ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
739 utxo.pushKV("scriptPubKey", o);
740 utxos.push_back(utxo);
741 }
742 objGetUTXOResponse.pushKV("utxos", utxos);
743
744 // return json string
745 std::string strJSON = objGetUTXOResponse.write() + "\n";
746 req->WriteHeader("Content-Type", "application/json");
747 req->WriteReply(HTTP_OK, strJSON);
748 return true;
749 }
750 default: {
751 return RESTERR(req, HTTP_NOT_FOUND,
752 "output format not found (available: " +
754 }
755 }
756}
757
758static bool rest_blockhash_by_height(Config &config, const std::any &context,
759 HTTPRequest *req,
760 const std::string &str_uri_part) {
761 if (!CheckWarmup(req)) {
762 return false;
763 }
764 std::string height_str;
765 const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
766
767 int32_t blockheight;
768 if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
769 return RESTERR(req, HTTP_BAD_REQUEST,
770 "Invalid height: " + SanitizeString(height_str));
771 }
772
773 CBlockIndex *pblockindex = nullptr;
774 {
775 ChainstateManager *maybe_chainman = GetChainman(context, req);
776 if (!maybe_chainman) {
777 return false;
778 }
779 ChainstateManager &chainman = *maybe_chainman;
780 LOCK(cs_main);
781 const CChain &active_chain = chainman.ActiveChain();
782 if (blockheight > active_chain.Height()) {
783 return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
784 }
785 pblockindex = active_chain[blockheight];
786 }
787 switch (rf) {
788 case RetFormat::BINARY: {
790 ss_blockhash << pblockindex->GetBlockHash();
791 req->WriteHeader("Content-Type", "application/octet-stream");
792 req->WriteReply(HTTP_OK, ss_blockhash.str());
793 return true;
794 }
795 case RetFormat::HEX: {
796 req->WriteHeader("Content-Type", "text/plain");
797 req->WriteReply(HTTP_OK,
798 pblockindex->GetBlockHash().GetHex() + "\n");
799 return true;
800 }
801 case RetFormat::JSON: {
802 req->WriteHeader("Content-Type", "application/json");
804 resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
805 req->WriteReply(HTTP_OK, resp.write() + "\n");
806 return true;
807 }
808 default: {
809 return RESTERR(req, HTTP_NOT_FOUND,
810 "output format not found (available: " +
812 }
813 }
814}
815
816static const struct {
817 const char *prefix;
818 bool (*handler)(Config &config, const std::any &context, HTTPRequest *req,
819 const std::string &strReq);
820} uri_prefixes[] = {
821 {"/rest/tx/", rest_tx},
822 {"/rest/block/notxdetails/", rest_block_notxdetails},
823 {"/rest/block/", rest_block_extended},
824 {"/rest/chaininfo", rest_chaininfo},
825 {"/rest/mempool/info", rest_mempool_info},
826 {"/rest/mempool/contents", rest_mempool_contents},
827 {"/rest/headers/", rest_headers},
828 {"/rest/getutxos", rest_getutxos},
829 {"/rest/blockhashbyheight/", rest_blockhash_by_height},
831
832void StartREST(const std::any &context) {
833 for (const auto &up : uri_prefixes) {
834 auto handler = [context, up](Config &config, HTTPRequest *req,
835 const std::string &prefix) {
836 return up.handler(config, context, req, prefix);
837 };
838 RegisterHTTPHandler(up.prefix, false, handler);
839 }
840}
841
843
844void StopREST() {
845 for (const auto &up : uri_prefixes) {
846 UnregisterHTTPHandler(up.prefix, false);
847 }
848}
RPCHelpMan getblockchaininfo()
UniValue blockToJSON(BlockManager &blockman, const CBlock &block, const CBlockIndex *tip, const CBlockIndex *blockindex, bool txDetails)
Block description to JSON.
Definition: blockchain.cpp:165
UniValue blockheaderToJSON(const CBlockIndex *tip, const CBlockIndex *blockindex)
Block header to JSON.
Definition: blockchain.cpp:131
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:146
An in-memory indexed chain of blocks.
Definition: chain.h:134
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:150
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:174
int Height() const
Return the maximal height in the chain.
Definition: chain.h:186
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:166
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:221
Abstract view on the open txout dataset.
Definition: coins.h:163
CCoinsView that brings transactions from a mempool into view.
Definition: txmempool.h:618
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:177
std::string str() const
Definition: streams.h:212
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:212
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:307
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:1219
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
Definition: validation.h:1343
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1435
SnapshotCompletionResult MaybeCompleteSnapshotValidation(std::function< void(bilingual_str)> shutdown_fnc=[](bilingual_str msg) { AbortNode(msg.original, msg);}) EXCLUSIVE_LOCKS_REQUIRED(Chainstate & ActiveChainstate() const
Once the background validation chainstate has reached the height which is the base of the UTXO snapsh...
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1432
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1429
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1351
A UTXO entry.
Definition: coins.h:28
Definition: config.h:19
In-flight HTTP request.
Definition: httpserver.h:71
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:707
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:695
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:671
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:587
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 TxToUniv(const CTransaction &tx, const BlockHash &hashBlock, UniValue &entry, bool include_hex=true, int serialize_flags=0, const CTxUndo *txundo=nullptr)
Definition: core_write.cpp:217
void ScriptPubKeyToUniv(const CScript &scriptPubKey, UniValue &out, bool fIncludeHex)
Definition: core_write.cpp:190
bool ParseHashStr(const std::string &strHex, uint256 &result)
Parse a hex string into 256 bits.
Definition: core_read.cpp:248
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:779
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:772
UniValue MempoolInfoToJSON(const CTxMemPool &pool)
Mempool information to JSON.
Definition: mempool.cpp:681
UniValue MempoolToJSON(const CTxMemPool &pool, bool verbose, bool include_mempool_sequence)
Mempool to JSON.
Definition: mempool.cpp:395
Definition: init.h:28
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:758
const char * prefix
Definition: rest.cpp:817
void StartREST(const std::any &context)
Start HTTP REST subsystem.
Definition: rest.cpp:832
bool(* handler)(Config &config, const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:818
void StopREST()
Stop HTTP REST subsystem.
Definition: rest.cpp:844
static bool rest_mempool_info(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:397
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:357
static bool rest_mempool_contents(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:427
RetFormat rf
Definition: rest.cpp:46
RetFormat
Definition: rest.cpp:38
void InterruptREST()
Interrupt RPC REST subsystem.
Definition: rest.cpp:842
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:363
static ChainstateManager * GetChainman(const std::any &context, HTTPRequest *req)
Get the node context chainstatemanager.
Definition: rest.cpp:119
static bool rest_block(const Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart, bool showTxDetails)
Definition: rest.cpp:277
static bool rest_chaininfo(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:369
static bool rest_tx(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:458
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:530
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
@ SER_NETWORK
Definition: serialize.h:152
#define READWRITE(...)
Definition: serialize.h:166
bool RPCIsInWarmup(std::string *outStatus)
Returns the current warmup state.
Definition: server.cpp:399
int RPCSerializationFlags()
Retrieves any serialization flags requested in command line argument.
Definition: server.cpp:679
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(Coin in)
Definition: rest.cpp:60
uint32_t nHeight
Definition: rest.cpp:56
CCoin()
Definition: rest.cpp:59
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:43
#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:16
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.
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11