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