Bitcoin ABC 0.32.6
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
27#include <univalue.h>
28
29#include <any>
30
33
34// Allow a max of 15 outpoints to be queried at once.
35static const size_t MAX_GETUTXOS_OUTPOINTS = 15;
36
37enum class RetFormat {
38 UNDEF,
39 BINARY,
40 HEX,
41 JSON,
42};
43
44static const struct {
46 const char *name;
47} rf_names[] = {
48 {RetFormat::UNDEF, ""},
49 {RetFormat::BINARY, "bin"},
50 {RetFormat::HEX, "hex"},
51 {RetFormat::JSON, "json"},
52};
53
54struct CCoin {
55 uint32_t nHeight{0};
57
58 CCoin() = default;
59 explicit CCoin(Coin in)
60 : nHeight(in.GetHeight()), out(std::move(in.GetTxOut())) {}
61
63 uint32_t nTxVerDummy = 0;
64 READWRITE(nTxVerDummy, obj.nHeight, obj.out);
65 }
66};
67
68static bool RESTERR(HTTPRequest *req, enum HTTPStatusCode status,
69 std::string message) {
70 req->WriteHeader("Content-Type", "text/plain");
71 req->WriteReply(status, message + "\r\n");
72 return false;
73}
74
82static NodeContext *GetNodeContext(const std::any &context, HTTPRequest *req) {
83 auto node_context = util::AnyPtr<NodeContext>(context);
84 if (!node_context) {
86 strprintf("%s:%d (%s)\n"
87 "Internal bug detected: Node context not found!\n"
88 "You may report this issue here: %s\n",
89 __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
90 return nullptr;
91 }
92 return node_context;
93}
94
102static CTxMemPool *GetMemPool(const std::any &context, HTTPRequest *req) {
103 auto node_context = util::AnyPtr<NodeContext>(context);
104 if (!node_context || !node_context->mempool) {
105 RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
106 return nullptr;
107 }
108 return node_context->mempool.get();
109}
110
118static ChainstateManager *GetChainman(const std::any &context,
119 HTTPRequest *req) {
120 auto node_context = util::AnyPtr<NodeContext>(context);
121 if (!node_context || !node_context->chainman) {
123 strprintf("%s:%d (%s)\n"
124 "Internal bug detected: Chainman disabled or instance"
125 " not found!\n"
126 "You may report this issue here: %s\n",
127 __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
128 return nullptr;
129 }
130 return node_context->chainman.get();
131}
132
133static RetFormat ParseDataFormat(std::string &param,
134 const std::string &strReq) {
135 const std::string::size_type pos = strReq.rfind('.');
136 if (pos == std::string::npos) {
137 param = strReq;
138 return rf_names[0].rf;
139 }
140
141 param = strReq.substr(0, pos);
142 const std::string suff(strReq, pos + 1);
143
144 for (const auto &rf_name : rf_names) {
145 if (suff == rf_name.name) {
146 return rf_name.rf;
147 }
148 }
149
150 /* If no suffix is found, return original string. */
151 param = strReq;
152 return rf_names[0].rf;
153}
154
155static std::string AvailableDataFormatsString() {
156 std::string formats;
157 for (const auto &rf_name : rf_names) {
158 if (strlen(rf_name.name) > 0) {
159 formats.append(".");
160 formats.append(rf_name.name);
161 formats.append(", ");
162 }
163 }
164
165 if (formats.length() > 0) {
166 return formats.substr(0, formats.length() - 2);
167 }
168
169 return formats;
170}
171
172static bool CheckWarmup(HTTPRequest *req) {
173 std::string statusmessage;
174 if (RPCIsInWarmup(&statusmessage)) {
176 "Service temporarily unavailable: " + statusmessage);
177 }
178
179 return true;
180}
181
182static bool rest_headers(Config &config, const std::any &context,
183 HTTPRequest *req, const std::string &strURIPart) {
184 if (!CheckWarmup(req)) {
185 return false;
186 }
187
188 std::string param;
189 const RetFormat rf = ParseDataFormat(param, strURIPart);
190 std::vector<std::string> path = SplitString(param, '/');
191
192 if (path.size() != 2) {
193 return RESTERR(req, HTTP_BAD_REQUEST,
194 "No header count specified. Use "
195 "/rest/headers/<count>/<hash>.<ext>.");
196 }
197
198 long count = strtol(path[0].c_str(), nullptr, 10);
199 if (count < 1 || count > 2000) {
200 return RESTERR(req, HTTP_BAD_REQUEST,
201 "Header count out of range: " + path[0]);
202 }
203
204 const std::string &hashStr = path[1];
205 uint256 rawHash;
206 if (!ParseHashStr(hashStr, rawHash)) {
207 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
208 }
209
210 const BlockHash hash(rawHash);
211
212 const CBlockIndex *tip = nullptr;
213 std::vector<const CBlockIndex *> headers;
214 headers.reserve(count);
215 {
216 ChainstateManager *maybe_chainman = GetChainman(context, req);
217 if (!maybe_chainman) {
218 return false;
219 }
220 ChainstateManager &chainman = *maybe_chainman;
221 LOCK(cs_main);
222 CChain &active_chain = chainman.ActiveChain();
223 tip = active_chain.Tip();
224 const CBlockIndex *pindex = chainman.m_blockman.LookupBlockIndex(hash);
225 while (pindex != nullptr && active_chain.Contains(pindex)) {
226 headers.push_back(pindex);
227 if (headers.size() == size_t(count)) {
228 break;
229 }
230 pindex = active_chain.Next(pindex);
231 }
232 }
233
234 switch (rf) {
235 case RetFormat::BINARY: {
236 DataStream ssHeader{};
237 for (const CBlockIndex *pindex : headers) {
238 ssHeader << pindex->GetBlockHeader();
239 }
240
241 std::string binaryHeader = ssHeader.str();
242 req->WriteHeader("Content-Type", "application/octet-stream");
243 req->WriteReply(HTTP_OK, binaryHeader);
244 return true;
245 }
246
247 case RetFormat::HEX: {
248 DataStream ssHeader{};
249 for (const CBlockIndex *pindex : headers) {
250 ssHeader << pindex->GetBlockHeader();
251 }
252
253 std::string strHex = HexStr(ssHeader) + "\n";
254 req->WriteHeader("Content-Type", "text/plain");
255 req->WriteReply(HTTP_OK, strHex);
256 return true;
257 }
258 case RetFormat::JSON: {
259 UniValue jsonHeaders(UniValue::VARR);
260 for (const CBlockIndex *pindex : headers) {
261 jsonHeaders.push_back(blockheaderToJSON(*tip, *pindex));
262 }
263 std::string strJSON = jsonHeaders.write() + "\n";
264 req->WriteHeader("Content-Type", "application/json");
265 req->WriteReply(HTTP_OK, strJSON);
266 return true;
267 }
268 default: {
269 return RESTERR(
270 req, HTTP_NOT_FOUND,
271 "output format not found (available: .bin, .hex, .json)");
272 }
273 }
274}
275
276static bool rest_block(const Config &config, const std::any &context,
277 HTTPRequest *req, const std::string &strURIPart,
278 TxVerbosity tx_verbosity) {
279 if (!CheckWarmup(req)) {
280 return false;
281 }
282
283 std::string hashStr;
284 const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
285
286 uint256 rawHash;
287 if (!ParseHashStr(hashStr, rawHash)) {
288 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
289 }
290
291 const BlockHash hash(rawHash);
292
293 CBlock block;
294 const CBlockIndex *pblockindex = nullptr;
295 const CBlockIndex *tip = nullptr;
296 ChainstateManager *maybe_chainman = GetChainman(context, req);
297 if (!maybe_chainman) {
298 return false;
299 }
300 ChainstateManager &chainman = *maybe_chainman;
301 {
302 LOCK(cs_main);
303 tip = chainman.ActiveTip();
304 pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
305 if (!pblockindex) {
306 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
307 }
308 if (chainman.m_blockman.IsBlockPruned(*pblockindex)) {
309 return RESTERR(req, HTTP_NOT_FOUND,
310 hashStr + " not available (pruned data)");
311 }
312 }
313 if (!chainman.m_blockman.ReadBlock(block, *pblockindex)) {
314 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
315 }
316
317 switch (rf) {
318 case RetFormat::BINARY: {
319 DataStream ssBlock{};
320 ssBlock << block;
321 std::string binaryBlock = ssBlock.str();
322 req->WriteHeader("Content-Type", "application/octet-stream");
323 req->WriteReply(HTTP_OK, binaryBlock);
324 return true;
325 }
326
327 case RetFormat::HEX: {
328 DataStream ssBlock{};
329 ssBlock << block;
330 std::string strHex = HexStr(ssBlock) + "\n";
331 req->WriteHeader("Content-Type", "text/plain");
332 req->WriteReply(HTTP_OK, strHex);
333 return true;
334 }
335
336 case RetFormat::JSON: {
337 UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip,
338 *pblockindex, tx_verbosity);
339 std::string strJSON = objBlock.write() + "\n";
340 req->WriteHeader("Content-Type", "application/json");
341 req->WriteReply(HTTP_OK, strJSON);
342 return true;
343 }
344
345 default: {
346 return RESTERR(req, HTTP_NOT_FOUND,
347 "output format not found (available: " +
349 }
350 }
351}
352
353static bool rest_block_extended(Config &config, const std::any &context,
354 HTTPRequest *req,
355 const std::string &strURIPart) {
356 return rest_block(config, context, req, strURIPart,
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, TxVerbosity::SHOW_TXID);
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", std::move(o));
735 utxos.push_back(std::move(utxo));
736 }
737 objGetUTXOResponse.pushKV("utxos", std::move(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}
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:1185
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:1436
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
Definition: validation.h:1317
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1443
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1440
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1437
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1326
A UTXO entry.
Definition: coins.h:29
Definition: config.h:19
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:118
std::string str() const
Definition: streams.h:140
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
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool ReadBlock(CBlock &block, const FlatFilePos &pos) const
Functions for disk access for blocks.
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:249
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:133
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
static bool rest_block(const Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart, TxVerbosity tx_verbosity)
Definition: rest.cpp:276
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:46
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:182
static bool rest_block_extended(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:353
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:45
RetFormat
Definition: rest.cpp:37
void InterruptREST()
Interrupt RPC REST subsystem.
Definition: rest.cpp:837
static bool RESTERR(HTTPRequest *req, enum HTTPStatusCode status, std::string message)
Definition: rest.cpp:68
static bool CheckWarmup(HTTPRequest *req)
Definition: rest.cpp:172
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:118
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:102
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:35
static std::string AvailableDataFormatsString()
Definition: rest.cpp:155
static NodeContext * GetNodeContext(const std::any &context, HTTPRequest *req)
Get the node context.
Definition: rest.cpp:82
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:176
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:54
CTxOut out
Definition: rest.cpp:56
CCoin()=default
CCoin(Coin in)
Definition: rest.cpp:59
uint32_t nHeight
Definition: rest.cpp:55
SERIALIZE_METHODS(CCoin, obj)
Definition: rest.cpp:62
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.