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