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