You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rest.cpp 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  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. #include "chain.h"
  6. #include "chainparams.h"
  7. #include "core_io.h"
  8. #include "primitives/block.h"
  9. #include "primitives/transaction.h"
  10. #include "validation.h"
  11. #include "httpserver.h"
  12. #include "rpc/blockchain.h"
  13. #include "rpc/server.h"
  14. #include "streams.h"
  15. #include "sync.h"
  16. #include "txmempool.h"
  17. #include "utilstrencodings.h"
  18. #include "version.h"
  19. #include <boost/algorithm/string.hpp>
  20. #include <univalue.h>
  21. static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
  22. enum RetFormat {
  23. RF_UNDEF,
  24. RF_BINARY,
  25. RF_HEX,
  26. RF_JSON,
  27. };
  28. static const struct {
  29. enum RetFormat rf;
  30. const char* name;
  31. } rf_names[] = {
  32. {RF_UNDEF, ""},
  33. {RF_BINARY, "bin"},
  34. {RF_HEX, "hex"},
  35. {RF_JSON, "json"},
  36. };
  37. struct CCoin {
  38. uint32_t nTxVer; // Don't call this nVersion, that name has a special meaning inside IMPLEMENT_SERIALIZE
  39. uint32_t nHeight;
  40. CTxOut out;
  41. ADD_SERIALIZE_METHODS;
  42. template <typename Stream, typename Operation>
  43. inline void SerializationOp(Stream& s, Operation ser_action)
  44. {
  45. READWRITE(nTxVer);
  46. READWRITE(nHeight);
  47. READWRITE(out);
  48. }
  49. };
  50. static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
  51. {
  52. req->WriteHeader("Content-Type", "text/plain");
  53. req->WriteReply(status, message + "\r\n");
  54. return false;
  55. }
  56. static enum RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
  57. {
  58. const std::string::size_type pos = strReq.rfind('.');
  59. if (pos == std::string::npos)
  60. {
  61. param = strReq;
  62. return rf_names[0].rf;
  63. }
  64. param = strReq.substr(0, pos);
  65. const std::string suff(strReq, pos + 1);
  66. for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
  67. if (suff == rf_names[i].name)
  68. return rf_names[i].rf;
  69. /* If no suffix is found, return original string. */
  70. param = strReq;
  71. return rf_names[0].rf;
  72. }
  73. static std::string AvailableDataFormatsString()
  74. {
  75. std::string formats = "";
  76. for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
  77. if (strlen(rf_names[i].name) > 0) {
  78. formats.append(".");
  79. formats.append(rf_names[i].name);
  80. formats.append(", ");
  81. }
  82. if (formats.length() > 0)
  83. return formats.substr(0, formats.length() - 2);
  84. return formats;
  85. }
  86. static bool ParseHashStr(const std::string& strReq, uint256& v)
  87. {
  88. if (!IsHex(strReq) || (strReq.size() != 64))
  89. return false;
  90. v.SetHex(strReq);
  91. return true;
  92. }
  93. static bool CheckWarmup(HTTPRequest* req)
  94. {
  95. std::string statusmessage;
  96. if (RPCIsInWarmup(&statusmessage))
  97. return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
  98. return true;
  99. }
  100. static bool rest_headers(HTTPRequest* req,
  101. const std::string& strURIPart)
  102. {
  103. if (!CheckWarmup(req))
  104. return false;
  105. std::string param;
  106. const RetFormat rf = ParseDataFormat(param, strURIPart);
  107. std::vector<std::string> path;
  108. boost::split(path, param, boost::is_any_of("/"));
  109. if (path.size() != 2)
  110. return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
  111. long count = strtol(path[0].c_str(), NULL, 10);
  112. if (count < 1 || count > 2000)
  113. return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
  114. std::string hashStr = path[1];
  115. uint256 hash;
  116. if (!ParseHashStr(hashStr, hash))
  117. return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
  118. std::vector<const CBlockIndex *> headers;
  119. headers.reserve(count);
  120. {
  121. LOCK(cs_main);
  122. BlockMap::const_iterator it = mapBlockIndex.find(hash);
  123. const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
  124. while (pindex != NULL && chainActive.Contains(pindex)) {
  125. headers.push_back(pindex);
  126. if (headers.size() == (unsigned long)count)
  127. break;
  128. pindex = chainActive.Next(pindex);
  129. }
  130. }
  131. CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
  132. BOOST_FOREACH(const CBlockIndex *pindex, headers) {
  133. ssHeader << pindex->GetBlockHeader();
  134. }
  135. switch (rf) {
  136. case RF_BINARY: {
  137. std::string binaryHeader = ssHeader.str();
  138. req->WriteHeader("Content-Type", "application/octet-stream");
  139. req->WriteReply(HTTP_OK, binaryHeader);
  140. return true;
  141. }
  142. case RF_HEX: {
  143. std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
  144. req->WriteHeader("Content-Type", "text/plain");
  145. req->WriteReply(HTTP_OK, strHex);
  146. return true;
  147. }
  148. case RF_JSON: {
  149. UniValue jsonHeaders(UniValue::VARR);
  150. BOOST_FOREACH(const CBlockIndex *pindex, headers) {
  151. jsonHeaders.push_back(blockheaderToJSON(pindex));
  152. }
  153. std::string strJSON = jsonHeaders.write() + "\n";
  154. req->WriteHeader("Content-Type", "application/json");
  155. req->WriteReply(HTTP_OK, strJSON);
  156. return true;
  157. }
  158. default: {
  159. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
  160. }
  161. }
  162. // not reached
  163. return true; // continue to process further HTTP reqs on this cxn
  164. }
  165. static bool rest_block(HTTPRequest* req,
  166. const std::string& strURIPart,
  167. bool showTxDetails)
  168. {
  169. if (!CheckWarmup(req))
  170. return false;
  171. std::string hashStr;
  172. const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
  173. uint256 hash;
  174. if (!ParseHashStr(hashStr, hash))
  175. return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
  176. CBlock block;
  177. CBlockIndex* pblockindex = NULL;
  178. {
  179. LOCK(cs_main);
  180. if (mapBlockIndex.count(hash) == 0)
  181. return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
  182. pblockindex = mapBlockIndex[hash];
  183. if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0)
  184. return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
  185. if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
  186. return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
  187. }
  188. CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
  189. ssBlock << block;
  190. switch (rf) {
  191. case RF_BINARY: {
  192. std::string binaryBlock = ssBlock.str();
  193. req->WriteHeader("Content-Type", "application/octet-stream");
  194. req->WriteReply(HTTP_OK, binaryBlock);
  195. return true;
  196. }
  197. case RF_HEX: {
  198. std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
  199. req->WriteHeader("Content-Type", "text/plain");
  200. req->WriteReply(HTTP_OK, strHex);
  201. return true;
  202. }
  203. case RF_JSON: {
  204. UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails);
  205. std::string strJSON = objBlock.write() + "\n";
  206. req->WriteHeader("Content-Type", "application/json");
  207. req->WriteReply(HTTP_OK, strJSON);
  208. return true;
  209. }
  210. default: {
  211. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
  212. }
  213. }
  214. // not reached
  215. return true; // continue to process further HTTP reqs on this cxn
  216. }
  217. static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart)
  218. {
  219. return rest_block(req, strURIPart, true);
  220. }
  221. static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPart)
  222. {
  223. return rest_block(req, strURIPart, false);
  224. }
  225. // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
  226. UniValue getblockchaininfo(const JSONRPCRequest& request);
  227. static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
  228. {
  229. if (!CheckWarmup(req))
  230. return false;
  231. std::string param;
  232. const RetFormat rf = ParseDataFormat(param, strURIPart);
  233. switch (rf) {
  234. case RF_JSON: {
  235. JSONRPCRequest jsonRequest;
  236. jsonRequest.params = UniValue(UniValue::VARR);
  237. UniValue chainInfoObject = getblockchaininfo(jsonRequest);
  238. std::string strJSON = chainInfoObject.write() + "\n";
  239. req->WriteHeader("Content-Type", "application/json");
  240. req->WriteReply(HTTP_OK, strJSON);
  241. return true;
  242. }
  243. default: {
  244. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
  245. }
  246. }
  247. // not reached
  248. return true; // continue to process further HTTP reqs on this cxn
  249. }
  250. static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
  251. {
  252. if (!CheckWarmup(req))
  253. return false;
  254. std::string param;
  255. const RetFormat rf = ParseDataFormat(param, strURIPart);
  256. switch (rf) {
  257. case RF_JSON: {
  258. UniValue mempoolInfoObject = mempoolInfoToJSON();
  259. std::string strJSON = mempoolInfoObject.write() + "\n";
  260. req->WriteHeader("Content-Type", "application/json");
  261. req->WriteReply(HTTP_OK, strJSON);
  262. return true;
  263. }
  264. default: {
  265. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
  266. }
  267. }
  268. // not reached
  269. return true; // continue to process further HTTP reqs on this cxn
  270. }
  271. static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart)
  272. {
  273. if (!CheckWarmup(req))
  274. return false;
  275. std::string param;
  276. const RetFormat rf = ParseDataFormat(param, strURIPart);
  277. switch (rf) {
  278. case RF_JSON: {
  279. UniValue mempoolObject = mempoolToJSON(true);
  280. std::string strJSON = mempoolObject.write() + "\n";
  281. req->WriteHeader("Content-Type", "application/json");
  282. req->WriteReply(HTTP_OK, strJSON);
  283. return true;
  284. }
  285. default: {
  286. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
  287. }
  288. }
  289. // not reached
  290. return true; // continue to process further HTTP reqs on this cxn
  291. }
  292. static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
  293. {
  294. if (!CheckWarmup(req))
  295. return false;
  296. std::string hashStr;
  297. const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
  298. uint256 hash;
  299. if (!ParseHashStr(hashStr, hash))
  300. return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
  301. CTransactionRef tx;
  302. uint256 hashBlock = uint256();
  303. if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
  304. return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
  305. CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
  306. ssTx << tx;
  307. switch (rf) {
  308. case RF_BINARY: {
  309. std::string binaryTx = ssTx.str();
  310. req->WriteHeader("Content-Type", "application/octet-stream");
  311. req->WriteReply(HTTP_OK, binaryTx);
  312. return true;
  313. }
  314. case RF_HEX: {
  315. std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
  316. req->WriteHeader("Content-Type", "text/plain");
  317. req->WriteReply(HTTP_OK, strHex);
  318. return true;
  319. }
  320. case RF_JSON: {
  321. UniValue objTx(UniValue::VOBJ);
  322. TxToUniv(*tx, hashBlock, objTx);
  323. std::string strJSON = objTx.write() + "\n";
  324. req->WriteHeader("Content-Type", "application/json");
  325. req->WriteReply(HTTP_OK, strJSON);
  326. return true;
  327. }
  328. default: {
  329. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
  330. }
  331. }
  332. // not reached
  333. return true; // continue to process further HTTP reqs on this cxn
  334. }
  335. static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
  336. {
  337. if (!CheckWarmup(req))
  338. return false;
  339. std::string param;
  340. const RetFormat rf = ParseDataFormat(param, strURIPart);
  341. std::vector<std::string> uriParts;
  342. if (param.length() > 1)
  343. {
  344. std::string strUriParams = param.substr(1);
  345. boost::split(uriParts, strUriParams, boost::is_any_of("/"));
  346. }
  347. // throw exception in case of a empty request
  348. std::string strRequestMutable = req->ReadBody();
  349. if (strRequestMutable.length() == 0 && uriParts.size() == 0)
  350. return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
  351. bool fInputParsed = false;
  352. bool fCheckMemPool = false;
  353. std::vector<COutPoint> vOutPoints;
  354. // parse/deserialize input
  355. // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
  356. if (uriParts.size() > 0)
  357. {
  358. //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
  359. if (uriParts.size() > 0 && uriParts[0] == "checkmempool")
  360. fCheckMemPool = true;
  361. for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
  362. {
  363. uint256 txid;
  364. int32_t nOutput;
  365. std::string strTxid = uriParts[i].substr(0, uriParts[i].find("-"));
  366. std::string strOutput = uriParts[i].substr(uriParts[i].find("-")+1);
  367. if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
  368. return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
  369. txid.SetHex(strTxid);
  370. vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
  371. }
  372. if (vOutPoints.size() > 0)
  373. fInputParsed = true;
  374. else
  375. return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
  376. }
  377. switch (rf) {
  378. case RF_HEX: {
  379. // convert hex to bin, continue then with bin part
  380. std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
  381. strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
  382. }
  383. case RF_BINARY: {
  384. try {
  385. //deserialize only if user sent a request
  386. if (strRequestMutable.size() > 0)
  387. {
  388. if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
  389. return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
  390. CDataStream oss(SER_NETWORK, PROTOCOL_VERSION);
  391. oss << strRequestMutable;
  392. oss >> fCheckMemPool;
  393. oss >> vOutPoints;
  394. }
  395. } catch (const std::ios_base::failure& e) {
  396. // abort in case of unreadable binary data
  397. return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
  398. }
  399. break;
  400. }
  401. case RF_JSON: {
  402. if (!fInputParsed)
  403. return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
  404. break;
  405. }
  406. default: {
  407. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
  408. }
  409. }
  410. // limit max outpoints
  411. if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
  412. return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
  413. // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
  414. std::vector<unsigned char> bitmap;
  415. std::vector<CCoin> outs;
  416. std::string bitmapStringRepresentation;
  417. std::vector<bool> hits;
  418. bitmap.resize((vOutPoints.size() + 7) / 8);
  419. {
  420. LOCK2(cs_main, mempool.cs);
  421. CCoinsView viewDummy;
  422. CCoinsViewCache view(&viewDummy);
  423. CCoinsViewCache& viewChain = *pcoinsTip;
  424. CCoinsViewMemPool viewMempool(&viewChain, mempool);
  425. if (fCheckMemPool)
  426. view.SetBackend(viewMempool); // switch cache backend to db+mempool in case user likes to query mempool
  427. for (size_t i = 0; i < vOutPoints.size(); i++) {
  428. CCoins coins;
  429. uint256 hash = vOutPoints[i].hash;
  430. bool hit = false;
  431. if (view.GetCoins(hash, coins)) {
  432. mempool.pruneSpent(hash, coins);
  433. if (coins.IsAvailable(vOutPoints[i].n)) {
  434. hit = true;
  435. // Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if
  436. // n is valid but points to an already spent output (IsNull).
  437. CCoin coin;
  438. coin.nTxVer = coins.nVersion;
  439. coin.nHeight = coins.nHeight;
  440. coin.out = coins.vout.at(vOutPoints[i].n);
  441. assert(!coin.out.IsNull());
  442. outs.push_back(coin);
  443. }
  444. }
  445. hits.push_back(hit);
  446. bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
  447. bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
  448. }
  449. }
  450. switch (rf) {
  451. case RF_BINARY: {
  452. // serialize data
  453. // use exact same output as mentioned in Bip64
  454. CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
  455. ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
  456. std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
  457. req->WriteHeader("Content-Type", "application/octet-stream");
  458. req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
  459. return true;
  460. }
  461. case RF_HEX: {
  462. CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
  463. ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs;
  464. std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
  465. req->WriteHeader("Content-Type", "text/plain");
  466. req->WriteReply(HTTP_OK, strHex);
  467. return true;
  468. }
  469. case RF_JSON: {
  470. UniValue objGetUTXOResponse(UniValue::VOBJ);
  471. // pack in some essentials
  472. // use more or less the same output as mentioned in Bip64
  473. objGetUTXOResponse.push_back(Pair("chainHeight", chainActive.Height()));
  474. objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex()));
  475. objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation));
  476. UniValue utxos(UniValue::VARR);
  477. BOOST_FOREACH (const CCoin& coin, outs) {
  478. UniValue utxo(UniValue::VOBJ);
  479. utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer));
  480. utxo.push_back(Pair("height", (int32_t)coin.nHeight));
  481. utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue)));
  482. // include the script in a json output
  483. UniValue o(UniValue::VOBJ);
  484. ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
  485. utxo.push_back(Pair("scriptPubKey", o));
  486. utxos.push_back(utxo);
  487. }
  488. objGetUTXOResponse.push_back(Pair("utxos", utxos));
  489. // return json string
  490. std::string strJSON = objGetUTXOResponse.write() + "\n";
  491. req->WriteHeader("Content-Type", "application/json");
  492. req->WriteReply(HTTP_OK, strJSON);
  493. return true;
  494. }
  495. default: {
  496. return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
  497. }
  498. }
  499. // not reached
  500. return true; // continue to process further HTTP reqs on this cxn
  501. }
  502. static const struct {
  503. const char* prefix;
  504. bool (*handler)(HTTPRequest* req, const std::string& strReq);
  505. } uri_prefixes[] = {
  506. {"/rest/tx/", rest_tx},
  507. {"/rest/block/notxdetails/", rest_block_notxdetails},
  508. {"/rest/block/", rest_block_extended},
  509. {"/rest/chaininfo", rest_chaininfo},
  510. {"/rest/mempool/info", rest_mempool_info},
  511. {"/rest/mempool/contents", rest_mempool_contents},
  512. {"/rest/headers/", rest_headers},
  513. {"/rest/getutxos", rest_getutxos},
  514. };
  515. bool StartREST()
  516. {
  517. for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
  518. RegisterHTTPHandler(uri_prefixes[i].prefix, false, uri_prefixes[i].handler);
  519. return true;
  520. }
  521. void InterruptREST()
  522. {
  523. }
  524. void StopREST()
  525. {
  526. for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
  527. UnregisterHTTPHandler(uri_prefixes[i].prefix, false);
  528. }