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/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/any.h>
25 #include <validation.h>
26 #include <version.h>
27 
28 #include <univalue.h>
29 
30 #include <any>
31 
33 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 (!chainman.m_blockman.ReadBlockFromDisk(block, *pblockindex)) {
316  return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
317  }
318 
319  switch (rf) {
320  case RetFormat::BINARY: {
321  CDataStream ssBlock(SER_NETWORK,
323  ssBlock << block;
324  std::string binaryBlock = ssBlock.str();
325  req->WriteHeader("Content-Type", "application/octet-stream");
326  req->WriteReply(HTTP_OK, binaryBlock);
327  return true;
328  }
329 
330  case RetFormat::HEX: {
331  CDataStream ssBlock(SER_NETWORK,
333  ssBlock << block;
334  std::string strHex = HexStr(ssBlock) + "\n";
335  req->WriteHeader("Content-Type", "text/plain");
336  req->WriteReply(HTTP_OK, strHex);
337  return true;
338  }
339 
340  case RetFormat::JSON: {
341  UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip,
342  pblockindex, showTxDetails);
343  std::string strJSON = objBlock.write() + "\n";
344  req->WriteHeader("Content-Type", "application/json");
345  req->WriteReply(HTTP_OK, strJSON);
346  return true;
347  }
348 
349  default: {
350  return RESTERR(req, HTTP_NOT_FOUND,
351  "output format not found (available: " +
353  }
354  }
355 }
356 
357 static bool rest_block_extended(Config &config, const std::any &context,
358  HTTPRequest *req,
359  const std::string &strURIPart) {
360  return rest_block(config, context, req, strURIPart, true);
361 }
362 
363 static bool rest_block_notxdetails(Config &config, const std::any &context,
364  HTTPRequest *req,
365  const std::string &strURIPart) {
366  return rest_block(config, context, req, strURIPart, false);
367 }
368 
369 static bool rest_chaininfo(Config &config, const std::any &context,
370  HTTPRequest *req, const std::string &strURIPart) {
371  if (!CheckWarmup(req)) {
372  return false;
373  }
374 
375  std::string param;
376  const RetFormat rf = ParseDataFormat(param, strURIPart);
377 
378  switch (rf) {
379  case RetFormat::JSON: {
380  JSONRPCRequest jsonRequest;
381  jsonRequest.context = context;
382  jsonRequest.params = UniValue(UniValue::VARR);
383  UniValue chainInfoObject =
384  getblockchaininfo().HandleRequest(config, jsonRequest);
385  std::string strJSON = chainInfoObject.write() + "\n";
386  req->WriteHeader("Content-Type", "application/json");
387  req->WriteReply(HTTP_OK, strJSON);
388  return true;
389  }
390  default: {
391  return RESTERR(req, HTTP_NOT_FOUND,
392  "output format not found (available: json)");
393  }
394  }
395 }
396 
397 static bool rest_mempool_info(Config &config, const std::any &context,
398  HTTPRequest *req, const std::string &strURIPart) {
399  if (!CheckWarmup(req)) {
400  return false;
401  }
402 
403  const CTxMemPool *mempool = GetMemPool(context, req);
404  if (!mempool) {
405  return false;
406  }
407 
408  std::string param;
409  const RetFormat rf = ParseDataFormat(param, strURIPart);
410 
411  switch (rf) {
412  case RetFormat::JSON: {
413  UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
414 
415  std::string strJSON = mempoolInfoObject.write() + "\n";
416  req->WriteHeader("Content-Type", "application/json");
417  req->WriteReply(HTTP_OK, strJSON);
418  return true;
419  }
420  default: {
421  return RESTERR(req, HTTP_NOT_FOUND,
422  "output format not found (available: json)");
423  }
424  }
425 }
426 
427 static bool rest_mempool_contents(Config &config, const std::any &context,
428  HTTPRequest *req,
429  const std::string &strURIPart) {
430  if (!CheckWarmup(req)) {
431  return false;
432  }
433 
434  const CTxMemPool *mempool = GetMemPool(context, req);
435  if (!mempool) {
436  return false;
437  }
438 
439  std::string param;
440  const RetFormat rf = ParseDataFormat(param, strURIPart);
441 
442  switch (rf) {
443  case RetFormat::JSON: {
444  UniValue mempoolObject = MempoolToJSON(*mempool, true);
445 
446  std::string strJSON = mempoolObject.write() + "\n";
447  req->WriteHeader("Content-Type", "application/json");
448  req->WriteReply(HTTP_OK, strJSON);
449  return true;
450  }
451  default: {
452  return RESTERR(req, HTTP_NOT_FOUND,
453  "output format not found (available: json)");
454  }
455  }
456 }
457 
458 static bool rest_tx(Config &config, const std::any &context, HTTPRequest *req,
459  const std::string &strURIPart) {
460  if (!CheckWarmup(req)) {
461  return false;
462  }
463 
464  std::string hashStr;
465  const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
466 
467  uint256 hash;
468  if (!ParseHashStr(hashStr, hash)) {
469  return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
470  }
471 
472  const TxId txid(hash);
473 
474  if (g_txindex) {
475  g_txindex->BlockUntilSyncedToCurrentChain();
476  }
477 
478  const NodeContext *const node = GetNodeContext(context, req);
479  if (!node) {
480  return false;
481  }
482  BlockHash hashBlock;
483  const CTransactionRef tx =
484  GetTransaction(/* block_index */ nullptr, node->mempool.get(), txid,
485  hashBlock, node->chainman->m_blockman);
486  if (!tx) {
487  return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
488  }
489 
490  switch (rf) {
491  case RetFormat::BINARY: {
494  ssTx << tx;
495 
496  std::string binaryTx = ssTx.str();
497  req->WriteHeader("Content-Type", "application/octet-stream");
498  req->WriteReply(HTTP_OK, binaryTx);
499  return true;
500  }
501 
502  case RetFormat::HEX: {
505  ssTx << tx;
506 
507  std::string strHex = HexStr(ssTx) + "\n";
508  req->WriteHeader("Content-Type", "text/plain");
509  req->WriteReply(HTTP_OK, strHex);
510  return true;
511  }
512 
513  case RetFormat::JSON: {
514  UniValue objTx(UniValue::VOBJ);
515  TxToUniv(*tx, hashBlock, objTx);
516  std::string strJSON = objTx.write() + "\n";
517  req->WriteHeader("Content-Type", "application/json");
518  req->WriteReply(HTTP_OK, strJSON);
519  return true;
520  }
521 
522  default: {
523  return RESTERR(req, HTTP_NOT_FOUND,
524  "output format not found (available: " +
526  }
527  }
528 }
529 
530 static bool rest_getutxos(Config &config, const std::any &context,
531  HTTPRequest *req, const std::string &strURIPart) {
532  if (!CheckWarmup(req)) {
533  return false;
534  }
535 
536  std::string param;
537  const RetFormat rf = ParseDataFormat(param, strURIPart);
538 
539  std::vector<std::string> uriParts;
540  if (param.length() > 1) {
541  std::string strUriParams = param.substr(1);
542  uriParts = SplitString(strUriParams, '/');
543  }
544 
545  // throw exception in case of an empty request
546  std::string strRequestMutable = req->ReadBody();
547  if (strRequestMutable.length() == 0 && uriParts.size() == 0) {
548  return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
549  }
550 
551  bool fInputParsed = false;
552  bool fCheckMemPool = false;
553  std::vector<COutPoint> vOutPoints;
554 
555  // parse/deserialize input
556  // input-format = output-format, rest/getutxos/bin requires binary input,
557  // gives binary output, ...
558 
559  if (uriParts.size() > 0) {
560  // inputs is sent over URI scheme
561  // (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
562  if (uriParts[0] == "checkmempool") {
563  fCheckMemPool = true;
564  }
565 
566  for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) {
567  int32_t nOutput;
568  std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
569  std::string strOutput =
570  uriParts[i].substr(uriParts[i].find('-') + 1);
571 
572  if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) {
573  return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
574  }
575 
576  TxId txid;
577  txid.SetHex(strTxid);
578  vOutPoints.push_back(COutPoint(txid, uint32_t(nOutput)));
579  }
580 
581  if (vOutPoints.size() > 0) {
582  fInputParsed = true;
583  } else {
584  return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
585  }
586  }
587 
588  switch (rf) {
589  case RetFormat::HEX: {
590  // convert hex to bin, continue then with bin part
591  std::vector<uint8_t> strRequestV = ParseHex(strRequestMutable);
592  strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
593  }
594  // FALLTHROUGH
595  case RetFormat::BINARY: {
596  try {
597  // deserialize only if user sent a request
598  if (strRequestMutable.size() > 0) {
599  // don't allow sending input over URI and HTTP RAW DATA
600  if (fInputParsed) {
601  return RESTERR(req, HTTP_BAD_REQUEST,
602  "Combination of URI scheme inputs and "
603  "raw post data is not allowed");
604  }
605 
607  oss << strRequestMutable;
608  oss >> fCheckMemPool;
609  oss >> vOutPoints;
610  }
611  } catch (const std::ios_base::failure &) {
612  // abort in case of unreadable binary data
613  return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
614  }
615  break;
616  }
617 
618  case RetFormat::JSON: {
619  if (!fInputParsed) {
620  return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
621  }
622  break;
623  }
624  default: {
625  return RESTERR(req, HTTP_NOT_FOUND,
626  "output format not found (available: " +
628  }
629  }
630 
631  // limit max outpoints
632  if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) {
633  return RESTERR(
634  req, HTTP_BAD_REQUEST,
635  strprintf("Error: max outpoints exceeded (max: %d, tried: %d)",
636  MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
637  }
638 
639  // check spentness and form a bitmap (as well as a JSON capable
640  // human-readable string representation)
641  std::vector<uint8_t> bitmap;
642  std::vector<CCoin> outs;
643  std::string bitmapStringRepresentation;
644  std::vector<bool> hits;
645  bitmap.resize((vOutPoints.size() + 7) / 8);
646  ChainstateManager *maybe_chainman = GetChainman(context, req);
647  if (!maybe_chainman) {
648  return false;
649  }
650  ChainstateManager &chainman = *maybe_chainman;
651  decltype(chainman.ActiveHeight()) active_height;
652  BlockHash active_hash;
653  {
654  auto process_utxos =
655  [&vOutPoints, &outs, &hits, &active_height, &active_hash,
656  &chainman](const CCoinsView &view, const CTxMemPool *mempool)
657  EXCLUSIVE_LOCKS_REQUIRED(chainman.GetMutex()) {
658  for (const COutPoint &vOutPoint : vOutPoints) {
659  Coin coin;
660  bool hit = (!mempool || !mempool->isSpent(vOutPoint)) &&
661  view.GetCoin(vOutPoint, coin);
662  hits.push_back(hit);
663  if (hit) {
664  outs.emplace_back(std::move(coin));
665  }
666  }
667  active_height = chainman.ActiveHeight();
668  active_hash = chainman.ActiveTip()->GetBlockHash();
669  };
670 
671  if (fCheckMemPool) {
672  const CTxMemPool *mempool = GetMemPool(context, req);
673  if (!mempool) {
674  return false;
675  }
676 
677  // use db+mempool as cache backend in case user likes to query
678  // mempool
679  LOCK2(cs_main, mempool->cs);
680  CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
681  CCoinsViewMemPool viewMempool(&viewChain, *mempool);
682  process_utxos(viewMempool, mempool);
683  } else {
684  // no need to lock mempool!
685  LOCK(cs_main);
686  process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr);
687  }
688 
689  for (size_t i = 0; i < hits.size(); ++i) {
690  const bool hit = hits[i];
691  // form a binary string representation (human-readable for json
692  // output)
693  bitmapStringRepresentation.append(hit ? "1" : "0");
694  bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
695  }
696  }
697 
698  switch (rf) {
699  case RetFormat::BINARY: {
700  // serialize data
701  // use exact same output as mentioned in Bip64
702  CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
703  ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
704  std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
705 
706  req->WriteHeader("Content-Type", "application/octet-stream");
707  req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
708  return true;
709  }
710 
711  case RetFormat::HEX: {
712  CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
713  ssGetUTXOResponse << active_height << active_hash << bitmap << 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", active_height);
727  objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex());
728  objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
729 
730  UniValue utxos(UniValue::VARR);
731  for (const CCoin &coin : outs) {
732  UniValue utxo(UniValue::VOBJ);
733  utxo.pushKV("height", int32_t(coin.nHeight));
734  utxo.pushKV("value", coin.out.nValue);
735 
736  // include the script in a json output
738  ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
739  utxo.pushKV("scriptPubKey", o);
740  utxos.push_back(utxo);
741  }
742  objGetUTXOResponse.pushKV("utxos", utxos);
743 
744  // return json string
745  std::string strJSON = objGetUTXOResponse.write() + "\n";
746  req->WriteHeader("Content-Type", "application/json");
747  req->WriteReply(HTTP_OK, strJSON);
748  return true;
749  }
750  default: {
751  return RESTERR(req, HTTP_NOT_FOUND,
752  "output format not found (available: " +
754  }
755  }
756 }
757 
758 static bool rest_blockhash_by_height(Config &config, const std::any &context,
759  HTTPRequest *req,
760  const std::string &str_uri_part) {
761  if (!CheckWarmup(req)) {
762  return false;
763  }
764  std::string height_str;
765  const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
766 
767  int32_t blockheight;
768  if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
769  return RESTERR(req, HTTP_BAD_REQUEST,
770  "Invalid height: " + SanitizeString(height_str));
771  }
772 
773  CBlockIndex *pblockindex = nullptr;
774  {
775  ChainstateManager *maybe_chainman = GetChainman(context, req);
776  if (!maybe_chainman) {
777  return false;
778  }
779  ChainstateManager &chainman = *maybe_chainman;
780  LOCK(cs_main);
781  const CChain &active_chain = chainman.ActiveChain();
782  if (blockheight > active_chain.Height()) {
783  return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
784  }
785  pblockindex = active_chain[blockheight];
786  }
787  switch (rf) {
788  case RetFormat::BINARY: {
790  ss_blockhash << pblockindex->GetBlockHash();
791  req->WriteHeader("Content-Type", "application/octet-stream");
792  req->WriteReply(HTTP_OK, ss_blockhash.str());
793  return true;
794  }
795  case RetFormat::HEX: {
796  req->WriteHeader("Content-Type", "text/plain");
797  req->WriteReply(HTTP_OK,
798  pblockindex->GetBlockHash().GetHex() + "\n");
799  return true;
800  }
801  case RetFormat::JSON: {
802  req->WriteHeader("Content-Type", "application/json");
804  resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
805  req->WriteReply(HTTP_OK, resp.write() + "\n");
806  return true;
807  }
808  default: {
809  return RESTERR(req, HTTP_NOT_FOUND,
810  "output format not found (available: " +
812  }
813  }
814 }
815 
816 static const struct {
817  const char *prefix;
818  bool (*handler)(Config &config, const std::any &context, HTTPRequest *req,
819  const std::string &strReq);
820 } uri_prefixes[] = {
821  {"/rest/tx/", rest_tx},
822  {"/rest/block/notxdetails/", rest_block_notxdetails},
823  {"/rest/block/", rest_block_extended},
824  {"/rest/chaininfo", rest_chaininfo},
825  {"/rest/mempool/info", rest_mempool_info},
826  {"/rest/mempool/contents", rest_mempool_contents},
827  {"/rest/headers/", rest_headers},
828  {"/rest/getutxos", rest_getutxos},
829  {"/rest/blockhashbyheight/", rest_blockhash_by_height},
830 };
831 
832 void StartREST(const std::any &context) {
833  for (const auto &up : uri_prefixes) {
834  auto handler = [context, up](Config &config, HTTPRequest *req,
835  const std::string &prefix) {
836  return up.handler(config, context, req, prefix);
837  };
838  RegisterHTTPHandler(up.prefix, false, handler);
839  }
840 }
841 
842 void InterruptREST() {}
843 
844 void StopREST() {
845  for (const auto &up : uri_prefixes) {
846  UnregisterHTTPHandler(up.prefix, false);
847  }
848 }
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
Definition: block.h:60
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
BlockHash GetBlockHash() const
Definition: blockindex.h:146
An in-memory indexed chain of blocks.
Definition: chain.h:134
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:174
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:150
int Height() const
Return the maximal height in the chain.
Definition: chain.h:186
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:166
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:221
Abstract view on the open txout dataset.
Definition: coins.h:163
CCoinsView that brings transactions from a mempool into view.
Definition: txmempool.h:591
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:1218
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1428
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
Definition: validation.h:1342
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1431
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1434
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1350
A UTXO entry.
Definition: coins.h:28
Definition: config.h:19
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
void push_back(UniValue val)
Definition: univalue.cpp:96
@ VOBJ
Definition: univalue.h:31
@ VARR
Definition: univalue.h:32
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:115
void SetHex(const char *psz)
Definition: uint256.cpp:24
std::string GetHex() const
Definition: uint256.cpp:16
bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos) const
Functions for disk access for blocks.
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:681
UniValue MempoolToJSON(const CTxMemPool &pool, bool verbose, bool include_mempool_sequence)
Mempool to JSON.
Definition: mempool.cpp:395
Definition: init.h:28
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const TxId &txid, BlockHash &hashBlock, const BlockManager &blockman)
Return transaction with a given txid.
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: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:758
const char * prefix
Definition: rest.cpp:817
void StartREST(const std::any &context)
Start HTTP REST subsystem.
Definition: rest.cpp:832
bool(* handler)(Config &config, const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:818
void StopREST()
Stop HTTP REST subsystem.
Definition: rest.cpp:844
static bool rest_mempool_info(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:397
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:357
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:427
RetFormat rf
Definition: rest.cpp:46
RetFormat
Definition: rest.cpp:38
void InterruptREST()
Interrupt RPC REST subsystem.
Definition: rest.cpp:842
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:363
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:369
static bool rest_tx(Config &config, const std::any &context, HTTPRequest *req, const std::string &strURIPart)
Definition: rest.cpp:458
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:530
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:152
#define READWRITE(...)
Definition: serialize.h:166
bool RPCIsInWarmup(std::string *outStatus)
Returns the current warmup state.
Definition: server.cpp:399
int RPCSerializationFlags()
Retrieves any serialization flags requested in command line argument.
Definition: server.cpp:679
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:22
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:43
#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:16
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11