1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2019 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 <core_io.h>
9 #include <httpserver.h>
10 #include <index/txindex.h>
11 #include <names/common.h>
12 #include <names/encoding.h>
13 #include <node/context.h>
14 #include <primitives/block.h>
15 #include <primitives/transaction.h>
16 #include <rpc/blockchain.h>
17 #include <rpc/names.h>
18 #include <rpc/protocol.h>
19 #include <rpc/server.h>
20 #include <streams.h>
21 #include <sync.h>
22 #include <txmempool.h>
23 #include <util/check.h>
24 #include <util/ref.h>
25 #include <util/strencodings.h>
26 #include <validation.h>
27 #include <version.h>
28 
29 #include <boost/algorithm/string.hpp>
30 
31 #include <univalue.h>
32 
33 static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
34 
35 enum class RetFormat {
36     UNDEF,
37     BINARY,
38     HEX,
39     JSON,
40 };
41 
42 static const struct {
43     RetFormat rf;
44     const char* name;
45 } rf_names[] = {
46       {RetFormat::UNDEF, ""},
47       {RetFormat::BINARY, "bin"},
48       {RetFormat::HEX, "hex"},
49       {RetFormat::JSON, "json"},
50 };
51 
52 struct CCoin {
53     uint32_t nHeight;
54     CTxOut out;
55 
CCoinCCoin56     CCoin() : nHeight(0) {}
CCoinCCoin57     explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
58 
SERIALIZE_METHODSCCoin59     SERIALIZE_METHODS(CCoin, obj)
60     {
61         uint32_t nTxVerDummy = 0;
62         READWRITE(nTxVerDummy, obj.nHeight, obj.out);
63     }
64 };
65 
RESTERR(HTTPRequest * req,enum HTTPStatusCode status,std::string message)66 static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
67 {
68     req->WriteHeader("Content-Type", "text/plain");
69     req->WriteReply(status, message + "\r\n");
70     return false;
71 }
72 
73 /**
74  * Get the node context.
75  *
76  * @param[in]  req  The HTTP request, whose status code will be set if node
77  *                  context is not found.
78  * @returns         Pointer to the node context or nullptr if not found.
79  */
GetNodeContext(const util::Ref & context,HTTPRequest * req)80 static NodeContext* GetNodeContext(const util::Ref& context, HTTPRequest* req)
81 {
82     NodeContext* node = context.Has<NodeContext>() ? &context.Get<NodeContext>() : nullptr;
83     if (!node) {
84         RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
85                 strprintf("%s:%d (%s)\n"
86                           "Internal bug detected: Node context not found!\n"
87                           "You may report this issue here: %s\n",
88                           __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
89         return nullptr;
90     }
91     return node;
92 }
93 
94 /**
95  * Get the node context mempool.
96  *
97  * @param[in]  req The HTTP request, whose status code will be set if node
98  *                 context mempool is not found.
99  * @returns        Pointer to the mempool or nullptr if no mempool found.
100  */
GetMemPool(const util::Ref & context,HTTPRequest * req)101 static CTxMemPool* GetMemPool(const util::Ref& context, HTTPRequest* req)
102 {
103     NodeContext* node = context.Has<NodeContext>() ? &context.Get<NodeContext>() : nullptr;
104     if (!node || !node->mempool) {
105         RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
106         return nullptr;
107     }
108     return node->mempool.get();
109 }
110 
ParseDataFormat(std::string & param,const std::string & strReq)111 static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
112 {
113     const std::string::size_type pos = strReq.rfind('.');
114     if (pos == std::string::npos)
115     {
116         param = strReq;
117         return rf_names[0].rf;
118     }
119 
120     param = strReq.substr(0, pos);
121     const std::string suff(strReq, pos + 1);
122 
123     for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
124         if (suff == rf_names[i].name)
125             return rf_names[i].rf;
126 
127     /* If no suffix is found, return original string.  */
128     param = strReq;
129     return rf_names[0].rf;
130 }
131 
AvailableDataFormatsString()132 static std::string AvailableDataFormatsString()
133 {
134     std::string formats;
135     for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
136         if (strlen(rf_names[i].name) > 0) {
137             formats.append(".");
138             formats.append(rf_names[i].name);
139             formats.append(", ");
140         }
141 
142     if (formats.length() > 0)
143         return formats.substr(0, formats.length() - 2);
144 
145     return formats;
146 }
147 
CheckWarmup(HTTPRequest * req)148 static bool CheckWarmup(HTTPRequest* req)
149 {
150     std::string statusmessage;
151     if (RPCIsInWarmup(&statusmessage))
152          return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
153     return true;
154 }
155 
DecodeName(valtype & decoded,const std::string & encoded)156 static bool DecodeName(valtype& decoded, const std::string& encoded)
157 {
158     decoded.clear();
159     for (std::string::const_iterator i = encoded.begin(); i != encoded.end(); ++i)
160     {
161         switch (*i)
162         {
163         case '+':
164             decoded.push_back(' ');
165             continue;
166 
167         case '%':
168         {
169             if (i + 2 >= encoded.end())
170                 return false;
171             const std::string hexStr(i + 1, i + 3);
172             i += 2;
173 
174             int intChar = 0;
175             for (char c : hexStr)
176             {
177                 intChar <<= 4;
178 
179                 if (c >= '0' && c <= '9')
180                     intChar += c - '0';
181                 else
182                 {
183                     c |= (1 << 5);
184                     if (c >= 'a' && c <= 'f')
185                         intChar += c - 'a' + 10;
186                     else
187                         return false;
188                 }
189             }
190 
191             decoded.push_back(static_cast<char>(intChar));
192             continue;
193         }
194 
195         default:
196             decoded.push_back(*i);
197             continue;
198         }
199     }
200 
201     return true;
202 }
203 
rest_headers(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)204 static bool rest_headers(const util::Ref& context,
205                          HTTPRequest* req,
206                          const std::string& strURIPart)
207 {
208     if (!CheckWarmup(req))
209         return false;
210     std::string param;
211     const RetFormat rf = ParseDataFormat(param, strURIPart);
212     std::vector<std::string> path;
213     boost::split(path, param, boost::is_any_of("/"));
214 
215     if (path.size() != 2)
216         return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
217 
218     long count = strtol(path[0].c_str(), nullptr, 10);
219     if (count < 1 || count > 2000)
220         return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
221 
222     std::string hashStr = path[1];
223     uint256 hash;
224     if (!ParseHashStr(hashStr, hash))
225         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
226 
227     const CBlockIndex* tip = nullptr;
228     std::vector<const CBlockIndex *> headers;
229     headers.reserve(count);
230     {
231         LOCK(cs_main);
232         tip = ::ChainActive().Tip();
233         const CBlockIndex* pindex = LookupBlockIndex(hash);
234         while (pindex != nullptr && ::ChainActive().Contains(pindex)) {
235             headers.push_back(pindex);
236             if (headers.size() == (unsigned long)count)
237                 break;
238             pindex = ::ChainActive().Next(pindex);
239         }
240     }
241 
242     switch (rf) {
243     case RetFormat::BINARY: {
244         CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
245         for (const CBlockIndex *pindex : headers) {
246             ssHeader << pindex->GetBlockHeader(Params().GetConsensus());
247         }
248 
249         std::string binaryHeader = ssHeader.str();
250         req->WriteHeader("Content-Type", "application/octet-stream");
251         req->WriteReply(HTTP_OK, binaryHeader);
252         return true;
253     }
254 
255     case RetFormat::HEX: {
256         CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
257         for (const CBlockIndex *pindex : headers) {
258             ssHeader << pindex->GetBlockHeader(Params().GetConsensus());
259         }
260 
261         std::string strHex = HexStr(ssHeader) + "\n";
262         req->WriteHeader("Content-Type", "text/plain");
263         req->WriteReply(HTTP_OK, strHex);
264         return true;
265     }
266     case RetFormat::JSON: {
267         UniValue jsonHeaders(UniValue::VARR);
268         for (const CBlockIndex *pindex : headers) {
269             jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
270         }
271         std::string strJSON = jsonHeaders.write() + "\n";
272         req->WriteHeader("Content-Type", "application/json");
273         req->WriteReply(HTTP_OK, strJSON);
274         return true;
275     }
276     default: {
277         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex, .json)");
278     }
279     }
280 }
281 
rest_block(HTTPRequest * req,const std::string & strURIPart,bool showTxDetails)282 static bool rest_block(HTTPRequest* req,
283                        const std::string& strURIPart,
284                        bool showTxDetails)
285 {
286     if (!CheckWarmup(req))
287         return false;
288     std::string hashStr;
289     const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
290 
291     uint256 hash;
292     if (!ParseHashStr(hashStr, hash))
293         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
294 
295     CBlock block;
296     CBlockIndex* pblockindex = nullptr;
297     CBlockIndex* tip = nullptr;
298     {
299         LOCK(cs_main);
300         tip = ::ChainActive().Tip();
301         pblockindex = LookupBlockIndex(hash);
302         if (!pblockindex) {
303             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
304         }
305 
306         if (IsBlockPruned(pblockindex))
307             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
308 
309         if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
310             return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
311     }
312 
313     switch (rf) {
314     case RetFormat::BINARY: {
315         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
316         ssBlock << block;
317         std::string binaryBlock = ssBlock.str();
318         req->WriteHeader("Content-Type", "application/octet-stream");
319         req->WriteReply(HTTP_OK, binaryBlock);
320         return true;
321     }
322 
323     case RetFormat::HEX: {
324         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
325         ssBlock << block;
326         std::string strHex = HexStr(ssBlock) + "\n";
327         req->WriteHeader("Content-Type", "text/plain");
328         req->WriteReply(HTTP_OK, strHex);
329         return true;
330     }
331 
332     case RetFormat::JSON: {
333         UniValue objBlock = blockToJSON(block, tip, pblockindex, showTxDetails);
334         std::string strJSON = objBlock.write() + "\n";
335         req->WriteHeader("Content-Type", "application/json");
336         req->WriteReply(HTTP_OK, strJSON);
337         return true;
338     }
339 
340     default: {
341         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
342     }
343     }
344 }
345 
rest_block_extended(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)346 static bool rest_block_extended(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
347 {
348     return rest_block(req, strURIPart, true);
349 }
350 
rest_block_notxdetails(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)351 static bool rest_block_notxdetails(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
352 {
353     return rest_block(req, strURIPart, false);
354 }
355 
356 // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
357 RPCHelpMan getblockchaininfo();
358 
rest_chaininfo(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)359 static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
360 {
361     if (!CheckWarmup(req))
362         return false;
363     std::string param;
364     const RetFormat rf = ParseDataFormat(param, strURIPart);
365 
366     switch (rf) {
367     case RetFormat::JSON: {
368         JSONRPCRequest jsonRequest(context);
369         jsonRequest.params = UniValue(UniValue::VARR);
370         UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
371         std::string strJSON = chainInfoObject.write() + "\n";
372         req->WriteHeader("Content-Type", "application/json");
373         req->WriteReply(HTTP_OK, strJSON);
374         return true;
375     }
376     default: {
377         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
378     }
379     }
380 }
381 
rest_mempool_info(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)382 static bool rest_mempool_info(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
383 {
384     if (!CheckWarmup(req))
385         return false;
386     const CTxMemPool* mempool = GetMemPool(context, req);
387     if (!mempool) return false;
388     std::string param;
389     const RetFormat rf = ParseDataFormat(param, strURIPart);
390 
391     switch (rf) {
392     case RetFormat::JSON: {
393         UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool);
394 
395         std::string strJSON = mempoolInfoObject.write() + "\n";
396         req->WriteHeader("Content-Type", "application/json");
397         req->WriteReply(HTTP_OK, strJSON);
398         return true;
399     }
400     default: {
401         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
402     }
403     }
404 }
405 
rest_mempool_contents(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)406 static bool rest_mempool_contents(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
407 {
408     if (!CheckWarmup(req)) return false;
409     const CTxMemPool* mempool = GetMemPool(context, req);
410     if (!mempool) return false;
411     std::string param;
412     const RetFormat rf = ParseDataFormat(param, strURIPart);
413 
414     switch (rf) {
415     case RetFormat::JSON: {
416         UniValue mempoolObject = MempoolToJSON(*mempool, true);
417 
418         std::string strJSON = mempoolObject.write() + "\n";
419         req->WriteHeader("Content-Type", "application/json");
420         req->WriteReply(HTTP_OK, strJSON);
421         return true;
422     }
423     default: {
424         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
425     }
426     }
427 }
428 
rest_tx(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)429 static bool rest_tx(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
430 {
431     if (!CheckWarmup(req))
432         return false;
433     std::string hashStr;
434     const RetFormat rf = ParseDataFormat(hashStr, strURIPart);
435 
436     uint256 hash;
437     if (!ParseHashStr(hashStr, hash))
438         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
439 
440     if (g_txindex) {
441         g_txindex->BlockUntilSyncedToCurrentChain();
442     }
443 
444     const NodeContext* const node = GetNodeContext(context, req);
445     if (!node) return false;
446     uint256 hashBlock = uint256();
447     const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool.get(), hash, Params().GetConsensus(), hashBlock);
448     if (!tx) {
449         return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
450     }
451 
452     switch (rf) {
453     case RetFormat::BINARY: {
454         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
455         ssTx << tx;
456 
457         std::string binaryTx = ssTx.str();
458         req->WriteHeader("Content-Type", "application/octet-stream");
459         req->WriteReply(HTTP_OK, binaryTx);
460         return true;
461     }
462 
463     case RetFormat::HEX: {
464         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
465         ssTx << tx;
466 
467         std::string strHex = HexStr(ssTx) + "\n";
468         req->WriteHeader("Content-Type", "text/plain");
469         req->WriteReply(HTTP_OK, strHex);
470         return true;
471     }
472 
473     case RetFormat::JSON: {
474         UniValue objTx(UniValue::VOBJ);
475         TxToUniv(*tx, hashBlock, objTx);
476         std::string strJSON = objTx.write() + "\n";
477         req->WriteHeader("Content-Type", "application/json");
478         req->WriteReply(HTTP_OK, strJSON);
479         return true;
480     }
481 
482     default: {
483         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
484     }
485     }
486 }
487 
rest_getutxos(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)488 static bool rest_getutxos(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
489 {
490     if (!CheckWarmup(req))
491         return false;
492     std::string param;
493     const RetFormat rf = ParseDataFormat(param, strURIPart);
494 
495     std::vector<std::string> uriParts;
496     if (param.length() > 1)
497     {
498         std::string strUriParams = param.substr(1);
499         boost::split(uriParts, strUriParams, boost::is_any_of("/"));
500     }
501 
502     // throw exception in case of an empty request
503     std::string strRequestMutable = req->ReadBody();
504     if (strRequestMutable.length() == 0 && uriParts.size() == 0)
505         return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
506 
507     bool fInputParsed = false;
508     bool fCheckMemPool = false;
509     std::vector<COutPoint> vOutPoints;
510 
511     // parse/deserialize input
512     // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
513 
514     if (uriParts.size() > 0)
515     {
516         //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
517         if (uriParts[0] == "checkmempool") fCheckMemPool = true;
518 
519         for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
520         {
521             uint256 txid;
522             int32_t nOutput;
523             std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
524             std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1);
525 
526             if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
527                 return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
528 
529             txid.SetHex(strTxid);
530             vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
531         }
532 
533         if (vOutPoints.size() > 0)
534             fInputParsed = true;
535         else
536             return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
537     }
538 
539     switch (rf) {
540     case RetFormat::HEX: {
541         // convert hex to bin, continue then with bin part
542         std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
543         strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
544     }
545 
546     case RetFormat::BINARY: {
547         try {
548             //deserialize only if user sent a request
549             if (strRequestMutable.size() > 0)
550             {
551                 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
552                     return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
553 
554                 CDataStream oss(SER_NETWORK, PROTOCOL_VERSION);
555                 oss << strRequestMutable;
556                 oss >> fCheckMemPool;
557                 oss >> vOutPoints;
558             }
559         } catch (const std::ios_base::failure&) {
560             // abort in case of unreadable binary data
561             return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
562         }
563         break;
564     }
565 
566     case RetFormat::JSON: {
567         if (!fInputParsed)
568             return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
569         break;
570     }
571     default: {
572         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
573     }
574     }
575 
576     // limit max outpoints
577     if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
578         return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
579 
580     // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
581     std::vector<unsigned char> bitmap;
582     std::vector<CCoin> outs;
583     std::string bitmapStringRepresentation;
584     std::vector<bool> hits;
585     bitmap.resize((vOutPoints.size() + 7) / 8);
586     {
587         auto process_utxos = [&vOutPoints, &outs, &hits](const CCoinsView& view, const CTxMemPool& mempool) {
588             for (const COutPoint& vOutPoint : vOutPoints) {
589                 Coin coin;
590                 bool hit = !mempool.isSpent(vOutPoint) && view.GetCoin(vOutPoint, coin);
591                 hits.push_back(hit);
592                 if (hit) outs.emplace_back(std::move(coin));
593             }
594         };
595 
596         if (fCheckMemPool) {
597             const CTxMemPool* mempool = GetMemPool(context, req);
598             if (!mempool) return false;
599             // use db+mempool as cache backend in case user likes to query mempool
600             LOCK2(cs_main, mempool->cs);
601             CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip();
602             CCoinsViewMemPool viewMempool(&viewChain, *mempool);
603             process_utxos(viewMempool, *mempool);
604         } else {
605             LOCK(cs_main);  // no need to lock mempool!
606             process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool());
607         }
608 
609         for (size_t i = 0; i < hits.size(); ++i) {
610             const bool hit = hits[i];
611             bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
612             bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
613         }
614     }
615 
616     switch (rf) {
617     case RetFormat::BINARY: {
618         // serialize data
619         // use exact same output as mentioned in Bip64
620         CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
621         ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
622         std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
623 
624         req->WriteHeader("Content-Type", "application/octet-stream");
625         req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
626         return true;
627     }
628 
629     case RetFormat::HEX: {
630         CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
631         ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
632         std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
633 
634         req->WriteHeader("Content-Type", "text/plain");
635         req->WriteReply(HTTP_OK, strHex);
636         return true;
637     }
638 
639     case RetFormat::JSON: {
640         UniValue objGetUTXOResponse(UniValue::VOBJ);
641 
642         // pack in some essentials
643         // use more or less the same output as mentioned in Bip64
644         objGetUTXOResponse.pushKV("chainHeight", ::ChainActive().Height());
645         objGetUTXOResponse.pushKV("chaintipHash", ::ChainActive().Tip()->GetBlockHash().GetHex());
646         objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
647 
648         UniValue utxos(UniValue::VARR);
649         for (const CCoin& coin : outs) {
650             UniValue utxo(UniValue::VOBJ);
651             utxo.pushKV("height", (int32_t)coin.nHeight);
652             utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
653 
654             // include the script in a json output
655             UniValue o(UniValue::VOBJ);
656             ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
657             utxo.pushKV("scriptPubKey", o);
658             utxos.push_back(utxo);
659         }
660         objGetUTXOResponse.pushKV("utxos", utxos);
661 
662         // return json string
663         std::string strJSON = objGetUTXOResponse.write() + "\n";
664         req->WriteHeader("Content-Type", "application/json");
665         req->WriteReply(HTTP_OK, strJSON);
666         return true;
667     }
668     default: {
669         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
670     }
671     }
672 }
673 
rest_blockhash_by_height(const util::Ref & context,HTTPRequest * req,const std::string & str_uri_part)674 static bool rest_blockhash_by_height(const util::Ref& context, HTTPRequest* req,
675                        const std::string& str_uri_part)
676 {
677     if (!CheckWarmup(req)) return false;
678     std::string height_str;
679     const RetFormat rf = ParseDataFormat(height_str, str_uri_part);
680 
681     int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
682     if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
683         return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
684     }
685 
686     CBlockIndex* pblockindex = nullptr;
687     {
688         LOCK(cs_main);
689         if (blockheight > ::ChainActive().Height()) {
690             return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
691         }
692         pblockindex = ::ChainActive()[blockheight];
693     }
694     switch (rf) {
695     case RetFormat::BINARY: {
696         CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION);
697         ss_blockhash << pblockindex->GetBlockHash();
698         req->WriteHeader("Content-Type", "application/octet-stream");
699         req->WriteReply(HTTP_OK, ss_blockhash.str());
700         return true;
701     }
702     case RetFormat::HEX: {
703         req->WriteHeader("Content-Type", "text/plain");
704         req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
705         return true;
706     }
707     case RetFormat::JSON: {
708         req->WriteHeader("Content-Type", "application/json");
709         UniValue resp = UniValue(UniValue::VOBJ);
710         resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
711         req->WriteReply(HTTP_OK, resp.write() + "\n");
712         return true;
713     }
714     default: {
715         return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
716     }
717     }
718 }
719 
rest_name(const util::Ref & context,HTTPRequest * req,const std::string & strURIPart)720 static bool rest_name(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
721 {
722     if (!CheckWarmup(req))
723         return false;
724     std::string encodedName;
725     const RetFormat rf = ParseDataFormat(encodedName, strURIPart);
726 
727     valtype plainName;
728     if (!DecodeName(plainName, encodedName))
729         return RESTERR(req, HTTP_BAD_REQUEST,
730                        "Invalid encoded name: " + encodedName);
731 
732     CNameData data;
733     if (!::ChainstateActive ().CoinsTip ().GetName(plainName, data))
734         return RESTERR(req, HTTP_NOT_FOUND,
735                        EncodeNameForMessage (plainName) + " not found");
736 
737     switch (rf)
738     {
739     case RetFormat::BINARY:
740     {
741         const std::string val(data.getValue().begin(), data.getValue().end());
742         req->WriteHeader("Content-Type", "application/octet-stream");
743         req->WriteReply(HTTP_OK, val);
744         return true;
745     }
746 
747     case RetFormat::HEX:
748     {
749         const valtype& binVal = data.getValue();
750         const std::string hexVal = HexStr(binVal) + "\n";
751         req->WriteHeader("Content-Type", "text/plain");
752         req->WriteReply(HTTP_OK, hexVal);
753         return true;
754     }
755 
756     case RetFormat::JSON:
757     {
758         const UniValue NO_OPTIONS(UniValue::VOBJ);
759         const UniValue obj = getNameInfo(NO_OPTIONS, plainName, data);
760         const std::string strJSON = obj.write() + "\n";
761         req->WriteHeader("Content-Type", "application/json");
762         req->WriteReply(HTTP_OK, strJSON);
763         return true;
764     }
765 
766     default:
767         return RESTERR(req, HTTP_NOT_FOUND,
768                        "output format not found (available: "
769                         + AvailableDataFormatsString() + ")");
770     }
771 
772     // not reached
773     return true; // continue to process further HTTP reqs on this cxn
774 }
775 
776 static const struct {
777     const char* prefix;
778     bool (*handler)(const util::Ref& context, HTTPRequest* req, const std::string& strReq);
779 } uri_prefixes[] = {
780       {"/rest/tx/", rest_tx},
781       {"/rest/block/notxdetails/", rest_block_notxdetails},
782       {"/rest/block/", rest_block_extended},
783       {"/rest/chaininfo", rest_chaininfo},
784       {"/rest/mempool/info", rest_mempool_info},
785       {"/rest/mempool/contents", rest_mempool_contents},
786       {"/rest/headers/", rest_headers},
787       {"/rest/getutxos", rest_getutxos},
788       {"/rest/blockhashbyheight/", rest_blockhash_by_height},
789       {"/rest/name/", rest_name},
790 };
791 
StartREST(const util::Ref & context)792 void StartREST(const util::Ref& context)
793 {
794     for (const auto& up : uri_prefixes) {
795         auto handler = [&context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
796         RegisterHTTPHandler(up.prefix, false, handler);
797     }
798 }
799 
InterruptREST()800 void InterruptREST()
801 {
802 }
803 
StopREST()804 void StopREST()
805 {
806     for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
807         UnregisterHTTPHandler(uri_prefixes[i].prefix, false);
808 }
809