Bitcoin ABC 0.32.4
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 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 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, showTxDetails);
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, true);
358}
359
360static bool rest_block_notxdetails(Config &config, const std::any &context,
361 HTTPRequest *req,
362 const std::string &strURIPart) {
363 return rest_block(config, context, req, strURIPart, false);
364}
365
366static bool rest_chaininfo(Config &config, const std::any &context,
367 HTTPRequest *req, const std::string &strURIPart) {
368 if (!CheckWarmup(req)) {
369 return false;
370 }
371
372 std::string param;
373 const RetFormat rf = ParseDataFormat(param, strURIPart);
374
375 switch (rf) {
376 case RetFormat::JSON: {
377 JSONRPCRequest jsonRequest;
378 jsonRequest.context = context;
379 jsonRequest.params = UniValue(UniValue::VARR);
380 UniValue chainInfoObject =
381 getblockchaininfo().HandleRequest(config, jsonRequest);
382 std::string strJSON = chainInfoObject.write() + "\n";
383 req->WriteHeader("Content-Type", "application/json");
384 req->WriteReply(HTTP_OK, strJSON);
385 return true;
386 }
387 default: {
388 return RESTERR(req, HTTP_NOT_FOUND,
389 "output format not found (available: json)");
390 }
391 }
392}
393
394static bool rest_mempool_info(Config &config, const std::any &context,
395 HTTPRequest *req, const std::string &strURIPart) {
396 if (!CheckWarmup(req)) {
397 return false;
398 }
399
400 const CTxMemPool *mempool = GetMemPool(context, req);
401 if (!mempool) {
402 return false;
403 }
404
405 std::string param;
406 const RetFormat rf = ParseDataFormat(param, strURIPart);
407
408 switch (rf) {
409 case RetFormat::JSON: {
410 UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
411
412 std::string strJSON = mempoolInfoObject.write() + "\n";
413 req->WriteHeader("Content-Type", "application/json");
414 req->WriteReply(HTTP_OK, strJSON);
415 return true;
416 }
417 default: {
418 return RESTERR(req, HTTP_NOT_FOUND,
419 "output format not found (available: json)");
420 }
421 }
422}
423
424static bool rest_mempool_contents(Config &config, const std::any &context,
425 HTTPRequest *req,
426 const std::string &strURIPart) {
427 if (!CheckWarmup(req)) {
428 return false;
429 }
430
431 const CTxMemPool *mempool = GetMemPool(context, req);
432 if (!mempool) {
433 return false;
434 }
435
436 std::string param;
437 const RetFormat rf = ParseDataFormat(param, strURIPart);
438
439 switch (rf) {
440 case RetFormat::JSON: {
441 UniValue mempoolObject = MempoolToJSON(*mempool, true);
442
443 std::string strJSON = mempoolObject.write() + "\n";
444 req->WriteHeader("Content-Type", "application/json");
445 req->WriteReply(HTTP_OK, strJSON);
446 return true;
447 }
448 default: {
449 return RESTERR(req, HTTP_NOT_FOUND,
450 "output format not found (available: json)");
451 }
452 }
453}
454
455static bool rest_tx(Config &config, const std::any &context, HTTPRequest *req,
456 const std::string &strURIPart) {
457 if (!CheckWarmup(req)) {
458 return false;
459 }
460
461 std::string hashStr;
462 const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
463
464 uint256 hash;
465 if (!ParseHashStr(hashStr, hash)) {
466 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
467 }
468
469 const TxId txid(hash);
470
471 if (g_txindex) {
472 g_txindex->BlockUntilSyncedToCurrentChain();
473 }
474
475 const NodeContext *const node = GetNodeContext(context, req);
476 if (!node) {
477 return false;
478 }
479 BlockHash hashBlock;
480 const CTransactionRef tx =
481 GetTransaction(/* block_index */ nullptr, node->mempool.get(), txid,
482 hashBlock, node->chainman->m_blockman);
483 if (!tx) {
484 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
485 }
486
487 switch (rf) {
488 case RetFormat::BINARY: {
489 DataStream ssTx{};
490 ssTx << tx;
491
492 std::string binaryTx = ssTx.str();
493 req->WriteHeader("Content-Type", "application/octet-stream");
494 req->WriteReply(HTTP_OK, binaryTx);
495 return true;
496 }
497
498 case RetFormat::HEX: {
499 DataStream ssTx{};
500 ssTx << tx;
501
502 std::string strHex = HexStr(ssTx) + "\n";
503 req->WriteHeader("Content-Type", "text/plain");
504 req->WriteReply(HTTP_OK, strHex);
505 return true;
506 }
507
508 case RetFormat::JSON: {
510 TxToUniv(*tx, hashBlock, objTx);
511 std::string strJSON = objTx.write() + "\n";
512 req->WriteHeader("Content-Type", "application/json");
513 req->WriteReply(HTTP_OK, strJSON);
514 return true;
515 }
516
517 default: {
518 return RESTERR(req, HTTP_NOT_FOUND,
519 "output format not found (available: " +
521 }
522 }
523}
524
525static bool rest_getutxos(Config &config, const std::any &context,
526 HTTPRequest *req, const std::string &strURIPart) {
527 if (!CheckWarmup(req)) {
528 return false;
529 }
530
531 std::string param;
532 const RetFormat rf = ParseDataFormat(param, strURIPart);
533
534 std::vector<std::string> uriParts;
535 if (param.length() > 1) {
536 std::string strUriParams = param.substr(1);
537 uriParts = SplitString(strUriParams, '/');
538 }
539
540 // throw exception in case of an empty request
541 std::string strRequestMutable = req->ReadBody();
542 if (strRequestMutable.length() == 0 && uriParts.size() == 0) {
543 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
544 }
545
546 bool fInputParsed = false;
547 bool fCheckMemPool = false;
548 std::vector<COutPoint> vOutPoints;
549
550 // parse/deserialize input
551 // input-format = output-format, rest/getutxos/bin requires binary input,
552 // gives binary output, ...
553
554 if (uriParts.size() > 0) {
555 // inputs is sent over URI scheme
556 // (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
557 if (uriParts[0] == "checkmempool") {
558 fCheckMemPool = true;
559 }
560
561 for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) {
562 int32_t nOutput;
563 std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
564 std::string strOutput =
565 uriParts[i].substr(uriParts[i].find('-') + 1);
566
567 if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) {
568 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
569 }
570
571 TxId txid;
572 txid.SetHex(strTxid);
573 vOutPoints.push_back(COutPoint(txid, uint32_t(nOutput)));
574 }
575
576 if (vOutPoints.size() > 0) {
577 fInputParsed = true;
578 } else {
579 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
580 }
581 }
582
583 switch (rf) {
584 case RetFormat::HEX: {
585 // convert hex to bin, continue then with bin part
586 std::vector<uint8_t> strRequestV = ParseHex(strRequestMutable);
587 strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
588 }
589 // FALLTHROUGH
590 case RetFormat::BINARY: {
591 try {
592 // deserialize only if user sent a request
593 if (strRequestMutable.size() > 0) {
594 // don't allow sending input over URI and HTTP RAW DATA
595 if (fInputParsed) {
596 return RESTERR(req, HTTP_BAD_REQUEST,
597 "Combination of URI scheme inputs and "
598 "raw post data is not allowed");
599 }
600
601 DataStream oss{};
602 oss << strRequestMutable;
603 oss >> fCheckMemPool;
604 oss >> vOutPoints;
605 }
606 } catch (const std::ios_base::failure &) {
607 // abort in case of unreadable binary data
608 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
609 }
610 break;
611 }
612
613 case RetFormat::JSON: {
614 if (!fInputParsed) {
615 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
616 }
617 break;
618 }
619 default: {
620 return RESTERR(req, HTTP_NOT_FOUND,
621 "output format not found (available: " +
623 }
624 }
625
626 // limit max outpoints
627 if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) {
628 return RESTERR(
629 req, HTTP_BAD_REQUEST,
630 strprintf("Error: max outpoints exceeded (max: %d, tried: %d)",
631 MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
632 }
633
634 // check spentness and form a bitmap (as well as a JSON capable
635 // human-readable string representation)
636 std::vector<uint8_t> bitmap;
637 std::vector<CCoin> outs;
638 std::string bitmapStringRepresentation;
639 std::vector<bool> hits;
640 bitmap.resize((vOutPoints.size() + 7) / 8);
641 ChainstateManager *maybe_chainman = GetChainman(context, req);
642 if (!maybe_chainman) {
643 return false;
644 }
645 ChainstateManager &chainman = *maybe_chainman;
646 decltype(chainman.ActiveHeight()) active_height;
647 BlockHash active_hash;
648 {
649 auto process_utxos =
650 [&vOutPoints, &outs, &hits, &active_height, &active_hash,
651 &chainman](const CCoinsView &view, const CTxMemPool *mempool)
653 for (const COutPoint &vOutPoint : vOutPoints) {
654 Coin coin;
655 bool hit = (!mempool || !mempool->isSpent(vOutPoint)) &&
656 view.GetCoin(vOutPoint, coin);
657 hits.push_back(hit);
658 if (hit) {
659 outs.emplace_back(std::move(coin));
660 }
661 }
662 active_height = chainman.ActiveHeight();
663 active_hash = chainman.ActiveTip()->GetBlockHash();
664 };
665
666 if (fCheckMemPool) {
667 const CTxMemPool *mempool = GetMemPool(context, req);
668 if (!mempool) {
669 return false;
670 }
671
672 // use db+mempool as cache backend in case user likes to query
673 // mempool
674 LOCK2(cs_main, mempool->cs);
675 CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
676 CCoinsViewMemPool viewMempool(&viewChain, *mempool);
677 process_utxos(viewMempool, mempool);
678 } else {
679 // no need to lock mempool!
680 LOCK(cs_main);
681 process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr);
682 }
683
684 for (size_t i = 0; i < hits.size(); ++i) {
685 const bool hit = hits[i];
686 // form a binary string representation (human-readable for json
687 // output)
688 bitmapStringRepresentation.append(hit ? "1" : "0");
689 bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
690 }
691 }
692
693 switch (rf) {
694 case RetFormat::BINARY: {
695 // serialize data
696 // use exact same output as mentioned in Bip64
697 DataStream ssGetUTXOResponse{};
698 ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
699 std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
700
701 req->WriteHeader("Content-Type", "application/octet-stream");
702 req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
703 return true;
704 }
705
706 case RetFormat::HEX: {
707 DataStream ssGetUTXOResponse{};
708 ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
709 std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
710
711 req->WriteHeader("Content-Type", "text/plain");
712 req->WriteReply(HTTP_OK, strHex);
713 return true;
714 }
715
716 case RetFormat::JSON: {
717 UniValue objGetUTXOResponse(UniValue::VOBJ);
718
719 // pack in some essentials
720 // use more or less the same output as mentioned in Bip64
721 objGetUTXOResponse.pushKV("chainHeight", active_height);
722 objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex());
723 objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
724
726 for (const CCoin &coin : outs) {
728 utxo.pushKV("height", int32_t(coin.nHeight));
729 utxo.pushKV("value", coin.out.nValue);
730
731 // include the script in a json output
733 ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
734 utxo.pushKV("scriptPubKey", o);
735 utxos.push_back(utxo);
736 }
737 objGetUTXOResponse.pushKV("utxos", utxos);
738
739 // return json string
740 std::string strJSON = objGetUTXOResponse.write() + "\n";
741 req->WriteHeader("Content-Type", "application/json");
742 req->WriteReply(HTTP_OK, strJSON);
743 return true;
744 }
745 default: {
746 return RESTERR(req, HTTP_NOT_FOUND,
747 "output format not found (available: " +
749 }
750 }
751}
752
753static bool rest_blockhash_by_height(Config &config, const std::any &context,
754 HTTPRequest *req,
755 const std::string &str_uri_part) {
756 if (!CheckWarmup(req)) {
757 return false;
758 }
759 std::string height_str;
760 const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
761
762 int32_t blockheight;
763 if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
764 return RESTERR(req, HTTP_BAD_REQUEST,
765 "Invalid height: " + SanitizeString(height_str));
766 }
767
768 CBlockIndex *pblockindex = nullptr;
769 {
770 ChainstateManager *maybe_chainman = GetChainman(context, req);
771 if (!maybe_chainman) {
772 return false;
773 }
774 ChainstateManager &chainman = *maybe_chainman;
775 LOCK(cs_main);
776 const CChain &active_chain = chainman.ActiveChain();
777 if (blockheight > active_chain.Height()) {
778 return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
779 }
780 pblockindex = active_chain[blockheight];
781 }
782 switch (rf) {
783 case RetFormat::BINARY: {
784 DataStream ss_blockhash{};
785 ss_blockhash << pblockindex->GetBlockHash();
786 req->WriteHeader("Content-Type", "application/octet-stream");
787 req->WriteReply(HTTP_OK, ss_blockhash.str());
788 return true;
789 }
790 case RetFormat::HEX: {
791 req->WriteHeader("Content-Type", "text/plain");
792 req->WriteReply(HTTP_OK,
793 pblockindex->GetBlockHash().GetHex() + "\n");
794 return true;
795 }
796 case RetFormat::JSON: {
797 req->WriteHeader("Content-Type", "application/json");
799 resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
800 req->WriteReply(HTTP_OK, resp.write() + "\n");
801 return true;
802 }
803 default: {
804 return RESTERR(req, HTTP_NOT_FOUND,
805 "output format not found (available: " +
807 }
808 }
809}
810
811static const struct {
812 const char *prefix;
813 bool (*handler)(Config &config, const std::any &context, HTTPRequest *req,
814 const std::string &strReq);
815} uri_prefixes[] = {
816 {"/rest/tx/", rest_tx},
817 {"/rest/block/notxdetails/", rest_block_notxdetails},
818 {"/rest/block/", rest_block_extended},
819 {"/rest/chaininfo", rest_chaininfo},
820 {"/rest/mempool/info", rest_mempool_info},
821 {"/rest/mempool/contents", rest_mempool_contents},
822 {"/rest/headers/", rest_headers},
823 {"/rest/getutxos", rest_getutxos},
824 {"/rest/blockhashbyheight/", rest_blockhash_by_height},
826
827void StartREST(const std::any &context) {
828 for (const auto &up : uri_prefixes) {
829 auto handler = [context, up](Config &config, HTTPRequest *req,
830 const std::string &prefix) {
831 return up.handler(config, context, req, prefix);
832 };
833 RegisterHTTPHandler(up.prefix, false, handler);
834 }
835}
836
838
839void StopREST() {
840 for (const auto &up : uri_prefixes) {
841 UnregisterHTTPHandler(up.prefix, false);
842 }
843}
UniValue blockToJSON(BlockManager &blockman, const CBlock &block, const CBlockIndex &tip, const CBlockIndex &blockindex, bool txDetails)
Block description to JSON.
Definition: blockchain.cpp:180
RPCHelpMan getblockchaininfo()
UniValue blockheaderToJSON(const CBlockIndex &tip, const CBlockIndex &blockindex)
Block header to JSON.
Definition: blockchain.cpp:146
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: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: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:173
std::string str() const
Definition: streams.h:195
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 TxToUniv(const CTransaction &tx, const BlockHash &hashBlock, UniValue &entry, bool include_hex=true, const CTxUndo *txundo=nullptr)
Definition: core_write.cpp:221
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
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:753
const char * prefix
Definition: rest.cpp:812
void StartREST(const std::any &context)
Start HTTP REST subsystem.
Definition: rest.cpp:827
bool(* handler)(Config &config, const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:813
void StopREST()
Stop HTTP REST subsystem.
Definition: rest.cpp:839
static bool rest_mempool_info(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:394
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:424
RetFormat rf
Definition: rest.cpp:46
RetFormat
Definition: rest.cpp:38
void InterruptREST()
Interrupt RPC REST subsystem.
Definition: rest.cpp:837
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:360
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:366
static bool rest_tx(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:455
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:525
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:168
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: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.