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