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