Bitcoin Core 31.99.0
P2P Digital Currency
rest.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-present 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 <rest.h>
7
8#include <blockfilter.h>
9#include <chain.h>
10#include <chainparams.h>
11#include <core_io.h>
12#include <flatfile.h>
13#include <httpserver.h>
15#include <index/txindex.h>
16#include <node/blockstorage.h>
17#include <node/context.h>
18#include <primitives/block.h>
20#include <rpc/blockchain.h>
21#include <rpc/mempool.h>
22#include <rpc/protocol.h>
23#include <rpc/server.h>
24#include <rpc/server_util.h>
25#include <streams.h>
26#include <sync.h>
27#include <txmempool.h>
28#include <undo.h>
29#include <util/any.h>
30#include <util/check.h>
31#include <util/overflow.h>
32#include <util/strencodings.h>
33#include <validation.h>
34
35#include <any>
36#include <vector>
37
38#include <univalue.h>
39
43
44static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
45static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000;
46
47// Cache-Control values for REST responses.
49static constexpr const char* REST_CACHE_IMMUTABLE = "public, immutable, max-age=86400";
51static constexpr const char* REST_CACHE_NO_STORE = "no-store";
52
53static const struct {
55 const char* name;
56} rf_names[] = {
61};
62
63struct CCoin {
64 uint32_t nHeight;
66
67 CCoin() : nHeight(0) {}
68 explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
69
71 {
72 uint32_t nTxVerDummy = 0;
73 READWRITE(nTxVerDummy, obj.nHeight, obj.out);
74 }
75};
76
77static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
78{
79 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
80 req->WriteHeader("Content-Type", "text/plain");
81 req->WriteReply(status, message + "\r\n");
82 return false;
83}
84
92static NodeContext* GetNodeContext(const std::any& context, HTTPRequest* req)
93{
94 auto node_context = util::AnyPtr<NodeContext>(context);
95 if (!node_context) {
96 RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, STR_INTERNAL_BUG("Node context not found!"));
97 return nullptr;
98 }
99 return node_context;
100}
101
109static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req)
110{
111 auto node_context = util::AnyPtr<NodeContext>(context);
112 if (!node_context || !node_context->mempool) {
113 RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
114 return nullptr;
115 }
116 return node_context->mempool.get();
117}
118
126static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
127{
128 auto node_context = util::AnyPtr<NodeContext>(context);
129 if (!node_context || !node_context->chainman) {
130 RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, STR_INTERNAL_BUG("Chainman disabled or instance not found!"));
131 return nullptr;
132 }
133 return node_context->chainman.get();
134}
135
136RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq)
137{
138 // Remove query string (if any, separated with '?') as it should not interfere with
139 // parsing param and data format
140 param = strReq.substr(0, strReq.rfind('?'));
141 const std::string::size_type pos_format{param.rfind('.')};
142
143 // No format string is found
144 if (pos_format == std::string::npos) {
146 }
147
148 // Match format string to available formats
149 const std::string suffix(param, pos_format + 1);
150 for (const auto& rf_name : rf_names) {
151 if (suffix == rf_name.name) {
152 param.erase(pos_format);
153 return rf_name.rf;
154 }
155 }
156
157 // If no suffix is found, return RESTResponseFormat::UNDEF and original string without query string
159}
160
161static std::string AvailableDataFormatsString()
162{
163 std::string formats;
164 for (const auto& rf_name : rf_names) {
165 if (strlen(rf_name.name) > 0) {
166 formats.append(".");
167 formats.append(rf_name.name);
168 formats.append(", ");
169 }
170 }
171
172 if (formats.length() > 0)
173 return formats.substr(0, formats.length() - 2);
174
175 return formats;
176}
177
178static bool CheckWarmup(HTTPRequest* req)
179{
180 std::string statusmessage;
181 if (RPCIsInWarmup(&statusmessage))
182 return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
183 return true;
184}
185
186static bool rest_headers(const std::any& context,
187 HTTPRequest* req,
188 const std::string& uri_part)
189{
190 if (!CheckWarmup(req))
191 return false;
192 std::string param;
193 const RESTResponseFormat rf = ParseDataFormat(param, uri_part);
194 std::vector<std::string> path = SplitString(param, '/');
195
196 std::string raw_count;
197 std::string hashStr;
198 if (path.size() == 2) {
199 // deprecated path: /rest/headers/<count>/<hash>
200 hashStr = path[1];
201 raw_count = path[0];
202 } else if (path.size() == 1) {
203 // new path with query parameter: /rest/headers/<hash>?count=<count>
204 hashStr = path[0];
205 try {
206 raw_count = req->GetQueryParameter("count").value_or("5");
207 } catch (const std::runtime_error& e) {
208 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
209 }
210 } else {
211 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
212 }
213
214 const auto parsed_count{ToIntegral<size_t>(raw_count)};
215 if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
216 return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
217 }
218
219 auto hash{uint256::FromHex(hashStr)};
220 if (!hash) {
221 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
222 }
223
224 const CBlockIndex* tip = nullptr;
225 std::vector<const CBlockIndex*> headers;
226 headers.reserve(*parsed_count);
227 ChainstateManager* maybe_chainman = GetChainman(context, req);
228 if (!maybe_chainman) return false;
229 ChainstateManager& chainman = *maybe_chainman;
230 {
231 LOCK(cs_main);
232 CChain& active_chain = chainman.ActiveChain();
233 tip = active_chain.Tip();
234 const CBlockIndex* pindex{chainman.m_blockman.LookupBlockIndex(*hash)};
235 while (pindex != nullptr && active_chain.Contains(*pindex)) {
236 headers.push_back(pindex);
237 if (headers.size() == *parsed_count) {
238 break;
239 }
240 pindex = active_chain.Next(*pindex);
241 }
242 }
243
244 switch (rf) {
246 DataStream ssHeader{};
247 for (const CBlockIndex *pindex : headers) {
248 ssHeader << pindex->GetBlockHeader();
249 }
250
251 // Do not cache because chain extensions and reorgs can affect the response.
252 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
253 req->WriteHeader("Content-Type", "application/octet-stream");
254 req->WriteReply(HTTP_OK, ssHeader);
255 return true;
256 }
257
259 DataStream ssHeader{};
260 for (const CBlockIndex *pindex : headers) {
261 ssHeader << pindex->GetBlockHeader();
262 }
263
264 std::string strHex = HexStr(ssHeader) + "\n";
265 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
266 req->WriteHeader("Content-Type", "text/plain");
267 req->WriteReply(HTTP_OK, strHex);
268 return true;
269 }
271 UniValue jsonHeaders(UniValue::VARR);
272 for (const CBlockIndex *pindex : headers) {
273 jsonHeaders.push_back(blockheaderToJSON(*tip, *pindex, chainman.GetConsensus().powLimit));
274 }
275 std::string strJSON = jsonHeaders.write() + "\n";
276 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
277 req->WriteHeader("Content-Type", "application/json");
278 req->WriteReply(HTTP_OK, strJSON);
279 return true;
280 }
281 default: {
282 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
283 }
284 }
285}
286
290static void SerializeBlockUndo(DataStream& stream, const CBlockUndo& block_undo)
291{
292 WriteCompactSize(stream, block_undo.vtxundo.size() + 1);
293 WriteCompactSize(stream, 0); // block_undo.vtxundo doesn't contain coinbase tx
294 for (const CTxUndo& tx_undo : block_undo.vtxundo) {
295 WriteCompactSize(stream, tx_undo.vprevout.size());
296 for (const Coin& coin : tx_undo.vprevout) {
297 coin.out.Serialize(stream);
298 }
299 }
300}
301
305static void BlockUndoToJSON(const CBlockUndo& block_undo, UniValue& result)
306{
307 result.push_back({UniValue::VARR}); // block_undo.vtxundo doesn't contain coinbase tx
308 for (const CTxUndo& tx_undo : block_undo.vtxundo) {
309 UniValue tx_prevouts(UniValue::VARR);
310 for (const Coin& coin : tx_undo.vprevout) {
311 UniValue prevout(UniValue::VOBJ);
312 prevout.pushKV("value", ValueFromAmount(coin.out.nValue));
313
314 UniValue script_pub_key(UniValue::VOBJ);
315 ScriptToUniv(coin.out.scriptPubKey, /*out=*/script_pub_key, /*include_hex=*/true, /*include_address=*/true);
316 prevout.pushKV("scriptPubKey", std::move(script_pub_key));
317
318 tx_prevouts.push_back(std::move(prevout));
319 }
320 result.push_back(std::move(tx_prevouts));
321 }
322}
323
324static bool rest_spent_txouts(const std::any& context, HTTPRequest* req, const std::string& uri_part)
325{
326 if (!CheckWarmup(req)) {
327 return false;
328 }
329 std::string param;
330 const RESTResponseFormat rf = ParseDataFormat(param, uri_part);
331 std::vector<std::string> path = SplitString(param, '/');
332
333 std::string hashStr;
334 if (path.size() == 1) {
335 // path with query parameter: /rest/spenttxouts/<hash>
336 hashStr = path[0];
337 } else {
338 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/spenttxouts/<hash>.<ext>");
339 }
340
341 auto hash{uint256::FromHex(hashStr)};
342 if (!hash) {
343 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
344 }
345
346 ChainstateManager* chainman = GetChainman(context, req);
347 if (!chainman) {
348 return false;
349 }
350
351 const CBlockIndex* pblockindex = WITH_LOCK(cs_main, return chainman->m_blockman.LookupBlockIndex(*hash));
352 if (!pblockindex) {
353 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
354 }
355
356 CBlockUndo block_undo;
357 if (pblockindex->nHeight > 0 && !chainman->m_blockman.ReadBlockUndo(block_undo, *pblockindex)) {
358 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " undo not available");
359 }
360
361 switch (rf) {
363 DataStream ssSpentResponse{};
364 SerializeBlockUndo(ssSpentResponse, block_undo);
365 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
366 req->WriteHeader("Content-Type", "application/octet-stream");
367 req->WriteReply(HTTP_OK, ssSpentResponse);
368 return true;
369 }
370
372 DataStream ssSpentResponse{};
373 SerializeBlockUndo(ssSpentResponse, block_undo);
374 const std::string strHex{HexStr(ssSpentResponse) + "\n"};
375 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
376 req->WriteHeader("Content-Type", "text/plain");
377 req->WriteReply(HTTP_OK, strHex);
378 return true;
379 }
380
382 UniValue result(UniValue::VARR);
383 BlockUndoToJSON(block_undo, result);
384 std::string strJSON = result.write() + "\n";
385 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
386 req->WriteHeader("Content-Type", "application/json");
387 req->WriteReply(HTTP_OK, strJSON);
388 return true;
389 }
390
391 default: {
392 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
393 }
394 }
395}
396
403static bool rest_block(const std::any& context,
404 HTTPRequest* req,
405 const std::string& uri_part,
406 std::optional<TxVerbosity> tx_verbosity,
407 std::optional<std::pair<size_t, size_t>> block_part = std::nullopt)
408{
409 if (!CheckWarmup(req))
410 return false;
411 std::string hashStr;
412 const RESTResponseFormat rf = ParseDataFormat(hashStr, uri_part);
413
414 auto hash{uint256::FromHex(hashStr)};
415 if (!hash) {
416 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
417 }
418
419 FlatFilePos pos{};
420 const CBlockIndex* pblockindex = nullptr;
421 const CBlockIndex* tip = nullptr;
422 ChainstateManager* maybe_chainman = GetChainman(context, req);
423 if (!maybe_chainman) return false;
424 ChainstateManager& chainman = *maybe_chainman;
425 {
426 LOCK(cs_main);
427 tip = chainman.ActiveChain().Tip();
428 pblockindex = chainman.m_blockman.LookupBlockIndex(*hash);
429 if (!pblockindex) {
430 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
431 }
432 if (!(pblockindex->nStatus & BLOCK_HAVE_DATA)) {
433 if (chainman.m_blockman.IsBlockPruned(*pblockindex)) {
434 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
435 }
436 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (not fully downloaded)");
437 }
438 pos = pblockindex->GetBlockPos();
439 }
440
441 const auto block_data{chainman.m_blockman.ReadRawBlock(pos, block_part)};
442 if (!block_data) {
443 switch (block_data.error()) {
444 case node::ReadRawError::IO: return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "I/O error reading " + hashStr);
446 assert(block_part);
447 return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Bad block part offset/size %d/%d for %s", block_part->first, block_part->second, hashStr));
448 } // no default case, so the compiler can warn about missing cases
449 assert(false);
450 }
451
452 switch (rf) {
454 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
455 req->WriteHeader("Content-Type", "application/octet-stream");
456 req->WriteReply(HTTP_OK, *block_data);
457 return true;
458 }
459
461 const std::string strHex{HexStr(*block_data) + "\n"};
462 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
463 req->WriteHeader("Content-Type", "text/plain");
464 req->WriteReply(HTTP_OK, strHex);
465 return true;
466 }
467
469 if (tx_verbosity) {
470 CBlock block{};
471 SpanReader{*block_data} >> TX_WITH_WITNESS(block);
472 UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, *tx_verbosity, chainman.GetConsensus().powLimit);
473 std::string strJSON = objBlock.write() + "\n";
474 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
475 req->WriteHeader("Content-Type", "application/json");
476 req->WriteReply(HTTP_OK, strJSON);
477 return true;
478 }
479 return RESTERR(req, HTTP_BAD_REQUEST, "JSON output is not supported for this request type");
480 }
481
482 default: {
483 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
484 }
485 }
486}
487
488static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& uri_part)
489{
490 return rest_block(context, req, uri_part, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
491}
492
493static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& uri_part)
494{
495 return rest_block(context, req, uri_part, TxVerbosity::SHOW_TXID);
496}
497
498static bool rest_block_part(const std::any& context, HTTPRequest* req, const std::string& uri_part)
499{
500 try {
501 if (const auto opt_offset{ToIntegral<size_t>(req->GetQueryParameter("offset").value_or(""))}) {
502 if (const auto opt_size{ToIntegral<size_t>(req->GetQueryParameter("size").value_or(""))}) {
503 return rest_block(context, req, uri_part,
504 /*tx_verbosity=*/std::nullopt,
505 /*block_part=*/{{*opt_offset, *opt_size}});
506 } else {
507 return RESTERR(req, HTTP_BAD_REQUEST, "Block part size missing or invalid");
508 }
509 } else {
510 return RESTERR(req, HTTP_BAD_REQUEST, "Block part offset missing or invalid");
511 }
512 } catch (const std::runtime_error& e) {
513 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
514 }
515}
516
517static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& uri_part)
518{
519 if (!CheckWarmup(req)) return false;
520
521 std::string param;
522 const RESTResponseFormat rf = ParseDataFormat(param, uri_part);
523
524 std::vector<std::string> uri_parts = SplitString(param, '/');
525 std::string raw_count;
526 std::string raw_blockhash;
527 if (uri_parts.size() == 3) {
528 // deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>
529 raw_blockhash = uri_parts[2];
530 raw_count = uri_parts[1];
531 } else if (uri_parts.size() == 2) {
532 // new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
533 raw_blockhash = uri_parts[1];
534 try {
535 raw_count = req->GetQueryParameter("count").value_or("5");
536 } catch (const std::runtime_error& e) {
537 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
538 }
539 } else {
540 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
541 }
542
543 const auto parsed_count{ToIntegral<size_t>(raw_count)};
544 if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
545 return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
546 }
547
548 auto block_hash{uint256::FromHex(raw_blockhash)};
549 if (!block_hash) {
550 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash);
551 }
552
553 BlockFilterType filtertype;
554 if (!BlockFilterTypeByName(uri_parts[0], filtertype)) {
555 return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]);
556 }
557
558 BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
559 if (!index) {
560 return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
561 }
562
563 std::vector<const CBlockIndex*> headers;
564 headers.reserve(*parsed_count);
565 {
566 ChainstateManager* maybe_chainman = GetChainman(context, req);
567 if (!maybe_chainman) return false;
568 ChainstateManager& chainman = *maybe_chainman;
569 LOCK(cs_main);
570 CChain& active_chain = chainman.ActiveChain();
571 const CBlockIndex* pindex{chainman.m_blockman.LookupBlockIndex(*block_hash)};
572 while (pindex != nullptr && active_chain.Contains(*pindex)) {
573 headers.push_back(pindex);
574 if (headers.size() == *parsed_count)
575 break;
576 pindex = active_chain.Next(*pindex);
577 }
578 }
579
580 bool index_ready = index->BlockUntilSyncedToCurrentChain();
581
582 std::vector<uint256> filter_headers;
583 filter_headers.reserve(*parsed_count);
584 for (const CBlockIndex* pindex : headers) {
585 uint256 filter_header;
586 if (!index->LookupFilterHeader(pindex, filter_header)) {
587 std::string errmsg = "Filter not found.";
588
589 if (!index_ready) {
590 errmsg += " Block filters are still in the process of being indexed.";
591 } else {
592 errmsg += " This error is unexpected and indicates index corruption.";
593 }
594
595 return RESTERR(req, HTTP_NOT_FOUND, errmsg);
596 }
597 filter_headers.push_back(filter_header);
598 }
599
600 switch (rf) {
602 DataStream ssHeader{};
603 for (const uint256& header : filter_headers) {
604 ssHeader << header;
605 }
606
607 // Do not cache because chain extensions and reorgs can affect the response.
608 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
609 req->WriteHeader("Content-Type", "application/octet-stream");
610 req->WriteReply(HTTP_OK, ssHeader);
611 return true;
612 }
614 DataStream ssHeader{};
615 for (const uint256& header : filter_headers) {
616 ssHeader << header;
617 }
618
619 std::string strHex = HexStr(ssHeader) + "\n";
620 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
621 req->WriteHeader("Content-Type", "text/plain");
622 req->WriteReply(HTTP_OK, strHex);
623 return true;
624 }
626 UniValue jsonHeaders(UniValue::VARR);
627 for (const uint256& header : filter_headers) {
628 jsonHeaders.push_back(header.GetHex());
629 }
630
631 std::string strJSON = jsonHeaders.write() + "\n";
632 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
633 req->WriteHeader("Content-Type", "application/json");
634 req->WriteReply(HTTP_OK, strJSON);
635 return true;
636 }
637 default: {
638 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
639 }
640 }
641}
642
643static bool rest_block_filter(const std::any& context, HTTPRequest* req, const std::string& uri_part)
644{
645 if (!CheckWarmup(req)) return false;
646
647 std::string param;
648 const RESTResponseFormat rf = ParseDataFormat(param, uri_part);
649
650 // request is sent over URI scheme /rest/blockfilter/filtertype/blockhash
651 std::vector<std::string> uri_parts = SplitString(param, '/');
652 if (uri_parts.size() != 2) {
653 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilter/<filtertype>/<blockhash>");
654 }
655
656 auto block_hash{uint256::FromHex(uri_parts[1])};
657 if (!block_hash) {
658 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[1]);
659 }
660
661 BlockFilterType filtertype;
662 if (!BlockFilterTypeByName(uri_parts[0], filtertype)) {
663 return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]);
664 }
665
666 BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
667 if (!index) {
668 return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
669 }
670
671 const CBlockIndex* block_index;
672 bool block_was_connected;
673 {
674 ChainstateManager* maybe_chainman = GetChainman(context, req);
675 if (!maybe_chainman) return false;
676 ChainstateManager& chainman = *maybe_chainman;
677 LOCK(cs_main);
678 block_index = chainman.m_blockman.LookupBlockIndex(*block_hash);
679 if (!block_index) {
680 return RESTERR(req, HTTP_NOT_FOUND, uri_parts[1] + " not found");
681 }
682 block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
683 }
684
685 bool index_ready = index->BlockUntilSyncedToCurrentChain();
686
687 BlockFilter filter;
688 if (!index->LookupFilter(block_index, filter)) {
689 std::string errmsg = "Filter not found.";
690
691 if (!block_was_connected) {
692 errmsg += " Block was not connected to active chain.";
693 } else if (!index_ready) {
694 errmsg += " Block filters are still in the process of being indexed.";
695 } else {
696 errmsg += " This error is unexpected and indicates index corruption.";
697 }
698
699 return RESTERR(req, HTTP_NOT_FOUND, errmsg);
700 }
701
702 switch (rf) {
704 DataStream ssResp{};
705 ssResp << filter;
706
707 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
708 req->WriteHeader("Content-Type", "application/octet-stream");
709 req->WriteReply(HTTP_OK, ssResp);
710 return true;
711 }
713 DataStream ssResp{};
714 ssResp << filter;
715
716 std::string strHex = HexStr(ssResp) + "\n";
717 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
718 req->WriteHeader("Content-Type", "text/plain");
719 req->WriteReply(HTTP_OK, strHex);
720 return true;
721 }
724 ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
725 std::string strJSON = ret.write() + "\n";
726 req->WriteHeader("Cache-Control", REST_CACHE_IMMUTABLE);
727 req->WriteHeader("Content-Type", "application/json");
728 req->WriteReply(HTTP_OK, strJSON);
729 return true;
730 }
731 default: {
732 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
733 }
734 }
735}
736
737// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
739
740static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std::string& uri_part)
741{
742 if (!CheckWarmup(req))
743 return false;
744 std::string param;
745 const RESTResponseFormat rf = ParseDataFormat(param, uri_part);
746
747 switch (rf) {
749 JSONRPCRequest jsonRequest;
750 jsonRequest.context = context;
751 jsonRequest.params = UniValue(UniValue::VARR);
752 UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
753 std::string strJSON = chainInfoObject.write() + "\n";
754 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
755 req->WriteHeader("Content-Type", "application/json");
756 req->WriteReply(HTTP_OK, strJSON);
757 return true;
758 }
759 default: {
760 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
761 }
762 }
763}
764
765
767
768static bool rest_deploymentinfo(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
769{
770 if (!CheckWarmup(req)) return false;
771
772 std::string hash_str;
773 const RESTResponseFormat rf = ParseDataFormat(hash_str, str_uri_part);
774 const bool current_tip{hash_str.empty()};
775
776 switch (rf) {
778 JSONRPCRequest jsonRequest;
779 jsonRequest.context = context;
780 jsonRequest.params = UniValue(UniValue::VARR);
781
782 if (!current_tip) {
783 auto hash{uint256::FromHex(hash_str)};
784 if (!hash) {
785 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hash_str);
786 }
787
788 const ChainstateManager* chainman = GetChainman(context, req);
789 if (!chainman) return false;
790 if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(*hash))) {
791 return RESTERR(req, HTTP_BAD_REQUEST, "Block not found");
792 }
793
794 jsonRequest.params.push_back(hash_str);
795 }
796
797 req->WriteHeader("Cache-Control", current_tip ? REST_CACHE_NO_STORE : REST_CACHE_IMMUTABLE);
798 req->WriteHeader("Content-Type", "application/json");
799 req->WriteReply(HTTP_OK, getdeploymentinfo().HandleRequest(jsonRequest).write() + "\n");
800 return true;
801 }
802 default: {
803 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
804 }
805 }
806
807}
808
809static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
810{
811 if (!CheckWarmup(req))
812 return false;
813
814 std::string param;
815 const RESTResponseFormat rf = ParseDataFormat(param, str_uri_part);
816 if (param != "contents" && param != "info") {
817 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/mempool/<info|contents>.json");
818 }
819
820 const CTxMemPool* mempool = GetMemPool(context, req);
821 if (!mempool) return false;
822
823 switch (rf) {
825 std::string str_json;
826 if (param == "contents") {
827 std::string raw_verbose;
828 try {
829 raw_verbose = req->GetQueryParameter("verbose").value_or("true");
830 } catch (const std::runtime_error& e) {
831 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
832 }
833 if (raw_verbose != "true" && raw_verbose != "false") {
834 return RESTERR(req, HTTP_BAD_REQUEST, "The \"verbose\" query parameter must be either \"true\" or \"false\".");
835 }
836 std::string raw_mempool_sequence;
837 try {
838 raw_mempool_sequence = req->GetQueryParameter("mempool_sequence").value_or("false");
839 } catch (const std::runtime_error& e) {
840 return RESTERR(req, HTTP_BAD_REQUEST, e.what());
841 }
842 if (raw_mempool_sequence != "true" && raw_mempool_sequence != "false") {
843 return RESTERR(req, HTTP_BAD_REQUEST, "The \"mempool_sequence\" query parameter must be either \"true\" or \"false\".");
844 }
845 const bool verbose{raw_verbose == "true"};
846 const bool mempool_sequence{raw_mempool_sequence == "true"};
847 if (verbose && mempool_sequence) {
848 return RESTERR(req, HTTP_BAD_REQUEST, "Verbose results cannot contain mempool sequence values. (hint: set \"verbose=false\")");
849 }
850 str_json = MempoolToJSON(*mempool, verbose, mempool_sequence).write() + "\n";
851 } else {
852 str_json = MempoolInfoToJSON(*mempool).write() + "\n";
853 }
854
855 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
856 req->WriteHeader("Content-Type", "application/json");
857 req->WriteReply(HTTP_OK, str_json);
858 return true;
859 }
860 default: {
861 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
862 }
863 }
864}
865
866static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string& uri_part)
867{
868 if (!CheckWarmup(req))
869 return false;
870 std::string hashStr;
871 const RESTResponseFormat rf = ParseDataFormat(hashStr, uri_part);
872
873 auto hash{Txid::FromHex(hashStr)};
874 if (!hash) {
875 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
876 }
877
878 if (g_txindex) {
879 g_txindex->BlockUntilSyncedToCurrentChain();
880 }
881
882 const NodeContext* const node = GetNodeContext(context, req);
883 if (!node) return false;
884 uint256 hashBlock = uint256();
885 const CTransactionRef tx{GetTransaction(/*block_index=*/nullptr, node->mempool.get(), *hash, node->chainman->m_blockman, hashBlock)};
886 if (!tx) {
887 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
888 }
889 switch (rf) {
891 DataStream ssTx;
892 ssTx << TX_WITH_WITNESS(tx);
893
894 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
895 req->WriteHeader("Content-Type", "application/octet-stream");
896 req->WriteReply(HTTP_OK, ssTx);
897 return true;
898 }
899
901 DataStream ssTx;
902 ssTx << TX_WITH_WITNESS(tx);
903
904 std::string strHex = HexStr(ssTx) + "\n";
905 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
906 req->WriteHeader("Content-Type", "text/plain");
907 req->WriteReply(HTTP_OK, strHex);
908 return true;
909 }
910
913 TxToUniv(*tx, /*block_hash=*/hashBlock, /*entry=*/ objTx);
914 std::string strJSON = objTx.write() + "\n";
915 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
916 req->WriteHeader("Content-Type", "application/json");
917 req->WriteReply(HTTP_OK, strJSON);
918 return true;
919 }
920
921 default: {
922 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
923 }
924 }
925}
926
927static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::string& uri_part)
928{
929 if (!CheckWarmup(req))
930 return false;
931 std::string param;
932 const RESTResponseFormat rf = ParseDataFormat(param, uri_part);
933
934 std::vector<std::string> uriParts;
935 if (param.length() > 1)
936 {
937 std::string strUriParams = param.substr(1);
938 uriParts = SplitString(strUriParams, '/');
939 }
940
941 // throw exception in case of an empty request
942 std::string strRequestMutable = req->ReadBody();
943 if (strRequestMutable.length() == 0 && uriParts.size() == 0)
944 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
945
946 bool fInputParsed = false;
947 bool fCheckMemPool = false;
948 std::vector<COutPoint> vOutPoints;
949
950 // parse/deserialize input
951 // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
952
953 if (uriParts.size() > 0)
954 {
955 //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
956 if (uriParts[0] == "checkmempool") fCheckMemPool = true;
957
958 for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
959 {
960 const auto txid_out{util::Split<std::string_view>(uriParts[i], '-')};
961 if (txid_out.size() != 2) {
962 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
963 }
964 auto txid{Txid::FromHex(txid_out.at(0))};
965 auto output{ToIntegral<uint32_t>(txid_out.at(1))};
966
967 if (!txid || !output) {
968 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
969 }
970
971 vOutPoints.emplace_back(*txid, *output);
972 }
973
974 if (vOutPoints.size() > 0)
975 fInputParsed = true;
976 else
977 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
978 }
979
980 switch (rf) {
982 // convert hex to bin, continue then with bin part
983 std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
984 strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
985 [[fallthrough]];
986 }
987
989 try {
990 //deserialize only if user sent a request
991 if (strRequestMutable.size() > 0)
992 {
993 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
994 return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
995
996 DataStream oss{};
997 oss << strRequestMutable;
998 oss >> fCheckMemPool;
999 oss >> vOutPoints;
1000 }
1001 } catch (const std::ios_base::failure&) {
1002 // abort in case of unreadable binary data
1003 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
1004 }
1005 break;
1006 }
1007
1009 if (!fInputParsed)
1010 return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
1011 break;
1012 }
1013 default: {
1014 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
1015 }
1016 }
1017
1018 // limit max outpoints
1019 if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
1020 return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
1021
1022 // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
1023 std::vector<unsigned char> bitmap;
1024 std::vector<CCoin> outs;
1025 std::string bitmapStringRepresentation;
1026 std::vector<bool> hits;
1027 bitmap.resize(CeilDiv(vOutPoints.size(), 8u));
1028 ChainstateManager* maybe_chainman = GetChainman(context, req);
1029 if (!maybe_chainman) return false;
1030 ChainstateManager& chainman = *maybe_chainman;
1031 decltype(chainman.ActiveHeight()) active_height;
1032 uint256 active_hash;
1033 {
1034 auto process_utxos = [&vOutPoints, &outs, &hits, &active_height, &active_hash, &chainman](const CCoinsView& view, const CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(chainman.GetMutex()) {
1035 for (const COutPoint& vOutPoint : vOutPoints) {
1036 auto coin = !mempool || !mempool->isSpent(vOutPoint) ? view.GetCoin(vOutPoint) : std::nullopt;
1037 hits.push_back(coin.has_value());
1038 if (coin) outs.emplace_back(std::move(*coin));
1039 }
1040 active_height = chainman.ActiveHeight();
1041 active_hash = chainman.ActiveTip()->GetBlockHash();
1042 };
1043
1044 if (fCheckMemPool) {
1045 const CTxMemPool* mempool = GetMemPool(context, req);
1046 if (!mempool) return false;
1047 // use db+mempool as cache backend in case user likes to query mempool
1048 LOCK2(cs_main, mempool->cs);
1049 CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip();
1050 CCoinsViewMemPool viewMempool(&viewChain, *mempool);
1051 process_utxos(viewMempool, mempool);
1052 } else {
1053 LOCK(cs_main);
1054 process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr);
1055 }
1056
1057 for (size_t i = 0; i < hits.size(); ++i) {
1058 const bool hit = hits[i];
1059 bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
1060 bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
1061 }
1062 }
1063
1064 switch (rf) {
1066 // serialize data
1067 // use exact same output as mentioned in Bip64
1068 DataStream ssGetUTXOResponse{};
1069 ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
1070
1071 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
1072 req->WriteHeader("Content-Type", "application/octet-stream");
1073 req->WriteReply(HTTP_OK, ssGetUTXOResponse);
1074 return true;
1075 }
1076
1078 DataStream ssGetUTXOResponse{};
1079 ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
1080 std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
1081
1082 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
1083 req->WriteHeader("Content-Type", "text/plain");
1084 req->WriteReply(HTTP_OK, strHex);
1085 return true;
1086 }
1087
1089 UniValue objGetUTXOResponse(UniValue::VOBJ);
1090
1091 // pack in some essentials
1092 // use more or less the same output as mentioned in Bip64
1093 objGetUTXOResponse.pushKV("chainHeight", active_height);
1094 objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex());
1095 objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
1096
1097 UniValue utxos(UniValue::VARR);
1098 for (const CCoin& coin : outs) {
1100 utxo.pushKV("height", coin.nHeight);
1101 utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
1102
1103 // include the script in a json output
1105 ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
1106 utxo.pushKV("scriptPubKey", std::move(o));
1107 utxos.push_back(std::move(utxo));
1108 }
1109 objGetUTXOResponse.pushKV("utxos", std::move(utxos));
1110
1111 // return json string
1112 std::string strJSON = objGetUTXOResponse.write() + "\n";
1113 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
1114 req->WriteHeader("Content-Type", "application/json");
1115 req->WriteReply(HTTP_OK, strJSON);
1116 return true;
1117 }
1118 default: {
1119 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
1120 }
1121 }
1122}
1123
1124static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
1125 const std::string& str_uri_part)
1126{
1127 if (!CheckWarmup(req)) return false;
1128 std::string height_str;
1129 const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part);
1130
1131 const auto blockheight{ToIntegral<int32_t>(height_str)};
1132 if (!blockheight || *blockheight < 0) {
1133 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str, SAFE_CHARS_URI));
1134 }
1135
1136 CBlockIndex* pblockindex = nullptr;
1137 {
1138 ChainstateManager* maybe_chainman = GetChainman(context, req);
1139 if (!maybe_chainman) return false;
1140 ChainstateManager& chainman = *maybe_chainman;
1141 LOCK(cs_main);
1142 const CChain& active_chain = chainman.ActiveChain();
1143 if (*blockheight > active_chain.Height()) {
1144 return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
1145 }
1146 pblockindex = active_chain[*blockheight];
1147 }
1148 switch (rf) {
1150 DataStream ss_blockhash{};
1151 ss_blockhash << pblockindex->GetBlockHash();
1152 // Do not cache because reorgs can change the response.
1153 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
1154 req->WriteHeader("Content-Type", "application/octet-stream");
1155 req->WriteReply(HTTP_OK, ss_blockhash);
1156 return true;
1157 }
1159 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
1160 req->WriteHeader("Content-Type", "text/plain");
1161 req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
1162 return true;
1163 }
1165 req->WriteHeader("Cache-Control", REST_CACHE_NO_STORE);
1166 req->WriteHeader("Content-Type", "application/json");
1168 resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
1169 req->WriteReply(HTTP_OK, resp.write() + "\n");
1170 return true;
1171 }
1172 default: {
1173 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
1174 }
1175 }
1176}
1177
1178static const struct {
1179 const char* prefix;
1180 bool (*handler)(const std::any& context, HTTPRequest* req, const std::string& strReq);
1181} uri_prefixes[] = {
1182 {"/rest/tx/", rest_tx},
1183 {"/rest/block/notxdetails/", rest_block_notxdetails},
1184 {"/rest/block/", rest_block_extended},
1185 {"/rest/blockpart/", rest_block_part},
1186 {"/rest/blockfilter/", rest_block_filter},
1187 {"/rest/blockfilterheaders/", rest_filter_header},
1188 {"/rest/chaininfo", rest_chaininfo},
1189 {"/rest/mempool/", rest_mempool},
1190 {"/rest/headers/", rest_headers},
1191 {"/rest/getutxos", rest_getutxos},
1192 {"/rest/deploymentinfo/", rest_deploymentinfo},
1193 {"/rest/deploymentinfo", rest_deploymentinfo},
1194 {"/rest/blockhashbyheight/", rest_blockhash_by_height},
1195 {"/rest/spenttxouts/", rest_spent_txouts},
1197
1198void StartREST(const std::any& context)
1199{
1200 for (const auto& up : uri_prefixes) {
1201 auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
1202 RegisterHTTPHandler(up.prefix, false, handler);
1203 }
1204}
1205
1207{
1208}
1209
1211{
1212 for (const auto& up : uri_prefixes) {
1213 UnregisterHTTPHandler(up.prefix, false);
1214 }
1215}
int ret
UniValue blockToJSON(BlockManager &blockman, const CBlock &block, const CBlockIndex &tip, const CBlockIndex &blockindex, TxVerbosity verbosity, const uint256 pow_limit)
Block description to JSON.
Definition: blockchain.cpp:212
UniValue blockheaderToJSON(const CBlockIndex &tip, const CBlockIndex &blockindex, const uint256 pow_limit)
Block header to JSON.
Definition: blockchain.cpp:164
bool BlockFilterTypeByName(std::string_view name, BlockFilterType &filter_type)
Find a filter type by its human-readable name.
BlockFilterType
Definition: blockfilter.h:94
BlockFilterIndex * GetBlockFilterIndex(BlockFilterType filter_type)
Get a block filter index by type.
@ BLOCK_VALID_SCRIPTS
Scripts & signatures ok.
Definition: chain.h:69
@ BLOCK_HAVE_DATA
full block available in blk*.dat
Definition: chain.h:75
#define STR_INTERNAL_BUG(msg)
Definition: check.h:99
Complete block filter struct as defined in BIP 157.
Definition: blockfilter.h:116
const std::vector< unsigned char > & GetEncodedFilter() const LIFETIMEBOUND
Definition: blockfilter.h:139
BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of bloc...
bool LookupFilter(const CBlockIndex *block_index, BlockFilter &filter_out) const
Get a single filter by block.
bool LookupFilterHeader(const CBlockIndex *block_index, uint256 &header_out) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_headers_cache)
Get a single filter header by block.
Definition: block.h:74
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:94
bool IsValid(enum BlockStatus nUpTo) const EXCLUSIVE_LOCKS_REQUIRED(
Check whether this block index entry is valid up to the passed validity level.
Definition: chain.h:250
uint256 GetBlockHash() const
Definition: chain.h:198
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:106
FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(
Definition: chain.h:163
Undo information for a CBlock.
Definition: undo.h:64
std::vector< CTxUndo > vtxundo
Definition: undo.h:66
An in-memory indexed chain of blocks.
Definition: chain.h:380
bool Contains(const CBlockIndex &index) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:410
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:396
CBlockIndex * Next(const CBlockIndex &index) 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:416
int Height() const
Return the maximal height in the chain.
Definition: chain.h:425
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:437
Pure abstract view on the open txout dataset.
Definition: coins.h:356
CCoinsView that brings transactions from a mempool into view.
Definition: txmempool.h:777
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:187
An output of a transaction.
Definition: transaction.h:140
CScript scriptPubKey
Definition: transaction.h:143
CAmount nValue
Definition: transaction.h:142
Undo information for a CTransaction.
Definition: undo.h:54
std::vector< Coin > vprevout
Definition: undo.h:57
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:694
Interface for managing multiple Chainstate objects, where each chainstate is associated with chainsta...
Definition: validation.h:950
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
Definition: validation.h:1042
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1180
Chainstate & ActiveChainstate() const
Alternatives to CurrentChainstate() used by older code to query latest chainstate information without...
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1179
const Consensus::Params & GetConsensus() const
Definition: validation.h:1018
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1178
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1048
A UTXO entry.
Definition: coins.h:46
CTxOut out
unspent transaction output
Definition: coins.h:49
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:165
void WriteHeader(std::string &&hdr, std::string &&value)
Definition: httpserver.cpp:703
std::optional< std::string > GetQueryParameter(std::string_view key) const
Definition: httpserver.cpp:666
std::string ReadBody() const
Definition: httpserver.h:190
void WriteReply(HTTPStatusCode status, std::span< const std::byte > reply_body={})
Definition: httpserver.cpp:535
UniValue params
Definition: request.h:57
std::any context
Definition: request.h:62
UniValue HandleRequest(const JSONRPCRequest &request) const
Definition: util.cpp:640
Minimal stream for reading from an existing byte array by std::span.
Definition: streams.h:83
void push_back(UniValue val)
Definition: univalue.cpp:103
@ VOBJ
Definition: univalue.h:24
@ VARR
Definition: univalue.h:24
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
std::string GetHex() const
Definition: uint256.cpp:11
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool ReadBlockUndo(CBlockUndo &blockundo, const CBlockIndex &index) const
ReadRawBlockResult ReadRawBlock(const FlatFilePos &pos, std::optional< std::pair< size_t, size_t > > block_part=std::nullopt) const
static std::optional< transaction_identifier > FromHex(std::string_view hex)
256-bit opaque blob.
Definition: uint256.h:196
static std::optional< uint256 > FromHex(std::string_view str)
Definition: uint256.h:198
void ScriptToUniv(const CScript &script, UniValue &out, bool include_hex, bool include_address, const SigningProvider *provider)
Definition: core_io.cpp:407
void TxToUniv(const CTransaction &tx, const uint256 &block_hash, UniValue &entry, bool include_hex, const CTxUndo *txundo, TxVerbosity verbosity, std::function< bool(const CTxOut &)> is_change_func)
Definition: core_io.cpp:428
UniValue ValueFromAmount(const CAmount amount)
Definition: core_io.cpp:283
@ SHOW_DETAILS_AND_PREVOUT
The same as previous option with information about prevouts if available.
@ SHOW_TXID
Only TXID for each block's transaction.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:248
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:241
HTTPHeaders headers
Definition: messages.h:21
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const Txid &hash, const BlockManager &blockman, uint256 &hashBlock)
Return transaction with a given hash.
std::vector< std::string > SplitString(std::string_view str, char sep)
Definition: string.h:153
constexpr auto CeilDiv(const Dividend dividend, const Divisor divisor)
Integer ceiling division (for unsigned values).
Definition: overflow.h:70
constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:180
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
static constexpr unsigned int MAX_REST_HEADERS_RESULTS
Definition: rest.cpp:45
static bool rest_headers(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:186
static void SerializeBlockUndo(DataStream &stream, const CBlockUndo &block_undo)
Serialize spent outputs as a list of per-transaction CTxOut lists using binary format.
Definition: rest.cpp:290
static bool rest_block_extended(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:488
static bool rest_blockhash_by_height(const std::any &context, HTTPRequest *req, const std::string &str_uri_part)
Definition: rest.cpp:1124
static bool rest_block_part(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:498
RESTResponseFormat rf
Definition: rest.cpp:54
static constexpr const char * REST_CACHE_IMMUTABLE
Response bytes never change.
Definition: rest.cpp:49
static bool rest_block_filter(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:643
const char * prefix
Definition: rest.cpp:1179
static bool rest_block_notxdetails(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:493
void StartREST(const std::any &context)
Start HTTP REST subsystem.
Definition: rest.cpp:1198
static bool rest_filter_header(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:517
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:1180
static bool rest_getutxos(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:927
static bool rest_tx(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:866
static bool rest_block(const std::any &context, HTTPRequest *req, const std::string &uri_part, std::optional< TxVerbosity > tx_verbosity, std::optional< std::pair< size_t, size_t > > block_part=std::nullopt)
This handler is used by multiple HTTP endpoints:
Definition: rest.cpp:403
static const struct @10 uri_prefixes[]
void StopREST()
Stop HTTP REST subsystem.
Definition: rest.cpp:1210
const char * name
Definition: rest.cpp:55
void InterruptREST()
Interrupt RPC REST subsystem.
Definition: rest.cpp:1206
static bool rest_deploymentinfo(const std::any &context, HTTPRequest *req, const std::string &str_uri_part)
Definition: rest.cpp:768
static bool RESTERR(HTTPRequest *req, enum HTTPStatusCode status, std::string message)
Definition: rest.cpp:77
static bool rest_mempool(const std::any &context, HTTPRequest *req, const std::string &str_uri_part)
Definition: rest.cpp:809
static const struct @9 rf_names[]
static constexpr const char * REST_CACHE_NO_STORE
Mutable, node-local, or error response; must not be cached.
Definition: rest.cpp:51
static bool CheckWarmup(HTTPRequest *req)
Definition: rest.cpp:178
static ChainstateManager * GetChainman(const std::any &context, HTTPRequest *req)
Get the node context chainstatemanager.
Definition: rest.cpp:126
RPCMethod getblockchaininfo()
static void BlockUndoToJSON(const CBlockUndo &block_undo, UniValue &result)
Serialize spent outputs as a list of per-transaction CTxOut lists using JSON format.
Definition: rest.cpp:305
static bool rest_chaininfo(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:740
RPCMethod getdeploymentinfo()
static bool rest_spent_txouts(const std::any &context, HTTPRequest *req, const std::string &uri_part)
Definition: rest.cpp:324
RESTResponseFormat ParseDataFormat(std::string &param, const std::string &strReq)
Parse a URI to get the data format and URI without data format and query string.
Definition: rest.cpp:136
static CTxMemPool * GetMemPool(const std::any &context, HTTPRequest *req)
Get the node context mempool.
Definition: rest.cpp:109
static const size_t MAX_GETUTXOS_OUTPOINTS
Definition: rest.cpp:44
static std::string AvailableDataFormatsString()
Definition: rest.cpp:161
static NodeContext * GetNodeContext(const std::any &context, HTTPRequest *req)
Get the node context.
Definition: rest.cpp:92
RESTResponseFormat
Definition: rest.h:10
UniValue MempoolInfoToJSON(const CTxMemPool &pool)
Mempool information to JSON.
Definition: mempool.cpp:1075
UniValue MempoolToJSON(const CTxMemPool &pool, bool verbose, bool include_mempool_sequence)
Mempool to JSON.
Definition: mempool.cpp:606
HTTPStatusCode
HTTP status codes.
Definition: protocol.h:11
@ HTTP_BAD_REQUEST
Definition: protocol.h:14
@ HTTP_OK
Definition: protocol.h:12
@ HTTP_SERVICE_UNAVAILABLE
Definition: protocol.h:21
@ HTTP_NOT_FOUND
Definition: protocol.h:17
@ HTTP_INTERNAL_SERVER_ERROR
Definition: protocol.h:20
void WriteCompactSize(SizeComputer &os, uint64_t nSize)
Definition: serialize.h:1151
#define READWRITE(...)
Definition: serialize.h:148
bool RPCIsInWarmup(std::string *outStatus)
Definition: server.cpp:704
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:69
@ SAFE_CHARS_URI
Chars allowed in URIs (RFC 3986)
Definition: strencodings.h:36
Definition: rest.cpp:63
CTxOut out
Definition: rest.cpp:65
CCoin(Coin &&in)
Definition: rest.cpp:68
uint32_t nHeight
Definition: rest.cpp:64
CCoin()
Definition: rest.cpp:67
SERIALIZE_METHODS(CCoin, obj)
Definition: rest.cpp:70
uint256 powLimit
Proof of work parameters.
Definition: params.h:116
NodeContext struct containing references to chain state and connection state.
Definition: context.h:59
#define LOCK2(cs1, cs2)
Definition: sync.h:269
#define LOCK(cs)
Definition: sync.h:268
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:299
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:41
std::string SanitizeString(std::string_view str, int rule)
Remove unsafe chars.
assert(!tx.IsCoinBase())