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