Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-2022 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 <rest.h>
7 : :
8 : : #include <blockfilter.h>
9 : : #include <chain.h>
10 : : #include <chainparams.h>
11 : : #include <core_io.h>
12 : : #include <httpserver.h>
13 : : #include <index/blockfilterindex.h>
14 : : #include <index/txindex.h>
15 : : #include <node/blockstorage.h>
16 : : #include <node/context.h>
17 [ + - ]: 2 : #include <primitives/block.h>
18 [ + - ]: 2 : #include <primitives/transaction.h>
19 : : #include <rpc/blockchain.h>
20 : : #include <rpc/mempool.h>
21 : : #include <rpc/protocol.h>
22 : : #include <rpc/server.h>
23 : : #include <rpc/server_util.h>
24 : : #include <streams.h>
25 : : #include <sync.h>
26 : : #include <txmempool.h>
27 : 2 : #include <util/any.h>
28 : : #include <util/check.h>
29 : : #include <validation.h>
30 : : #include <version.h>
31 : :
32 : : #include <any>
33 : : #include <string>
34 : :
35 : : #include <univalue.h>
36 : :
37 : : using node::GetTransaction;
38 : : using node::NodeContext;
39 : :
40 : : static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
41 : : static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000;
42 : :
43 : : static const struct {
44 : : RESTResponseFormat rf;
45 : : const char* name;
46 : : } rf_names[] = {
47 : : {RESTResponseFormat::UNDEF, ""},
48 : : {RESTResponseFormat::BINARY, "bin"},
49 : : {RESTResponseFormat::HEX, "hex"},
50 : : {RESTResponseFormat::JSON, "json"},
51 : : };
52 : :
53 : 0 : struct CCoin {
54 : : uint32_t nHeight;
55 : : CTxOut out;
56 : :
57 : : CCoin() : nHeight(0) {}
58 : 0 : explicit CCoin(Coin&& in) : nHeight(in.nHeight), out(std::move(in.out)) {}
59 : :
60 : 0 : SERIALIZE_METHODS(CCoin, obj)
61 : : {
62 : 0 : uint32_t nTxVerDummy = 0;
63 : 0 : READWRITE(nTxVerDummy, obj.nHeight, obj.out);
64 : 0 : }
65 : : };
66 : :
67 : 0 : static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string message)
68 : : {
69 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
70 [ # # ]: 0 : req->WriteReply(status, message + "\r\n");
71 : 0 : return false;
72 : 0 : }
73 : :
74 : : /**
75 : : * Get the node context.
76 : : *
77 : : * @param[in] req The HTTP request, whose status code will be set if node
78 : : * context is not found.
79 : : * @returns Pointer to the node context or nullptr if not found.
80 : : */
81 : 0 : static NodeContext* GetNodeContext(const std::any& context, HTTPRequest* req)
82 : : {
83 : 0 : auto node_context = util::AnyPtr<NodeContext>(context);
84 [ # # ]: 0 : if (!node_context) {
85 [ # # ][ # # ]: 0 : RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
86 : 0 : strprintf("%s:%d (%s)\n"
87 : : "Internal bug detected: Node context not found!\n"
88 : : "You may report this issue here: %s\n",
89 : 0 : __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
90 : 0 : return nullptr;
91 : : }
92 : 0 : return node_context;
93 : 0 : }
94 : :
95 : : /**
96 : : * Get the node context mempool.
97 : : *
98 : : * @param[in] req The HTTP request, whose status code will be set if node
99 : : * context mempool is not found.
100 : : * @returns Pointer to the mempool or nullptr if no mempool found.
101 : : */
102 : 0 : static CTxMemPool* GetMemPool(const std::any& context, HTTPRequest* req)
103 : : {
104 : 0 : auto node_context = util::AnyPtr<NodeContext>(context);
105 [ # # ][ # # ]: 0 : if (!node_context || !node_context->mempool) {
106 [ # # ][ # # ]: 0 : RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
107 : 0 : return nullptr;
108 : : }
109 : 0 : return node_context->mempool.get();
110 : 0 : }
111 : :
112 : : /**
113 : : * Get the node context chainstatemanager.
114 : : *
115 : : * @param[in] req The HTTP request, whose status code will be set if node
116 : : * context chainstatemanager is not found.
117 : : * @returns Pointer to the chainstatemanager or nullptr if none found.
118 : : */
119 : 0 : static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
120 : : {
121 : 0 : auto node_context = util::AnyPtr<NodeContext>(context);
122 [ # # ][ # # ]: 0 : if (!node_context || !node_context->chainman) {
123 [ # # ][ # # ]: 0 : RESTERR(req, HTTP_INTERNAL_SERVER_ERROR,
124 : 0 : strprintf("%s:%d (%s)\n"
125 : : "Internal bug detected: Chainman disabled or instance not found!\n"
126 : : "You may report this issue here: %s\n",
127 : 0 : __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
128 : 0 : return nullptr;
129 : : }
130 : 0 : return node_context->chainman.get();
131 : 0 : }
132 : :
133 : 0 : RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq)
134 : : {
135 : : // Remove query string (if any, separated with '?') as it should not interfere with
136 : : // parsing param and data format
137 : 0 : param = strReq.substr(0, strReq.rfind('?'));
138 : 0 : const std::string::size_type pos_format{param.rfind('.')};
139 : :
140 : : // No format string is found
141 [ # # ]: 0 : if (pos_format == std::string::npos) {
142 : 0 : return rf_names[0].rf;
143 : : }
144 : :
145 : : // Match format string to available formats
146 [ # # ]: 0 : const std::string suffix(param, pos_format + 1);
147 [ # # ]: 0 : for (const auto& rf_name : rf_names) {
148 [ # # ][ # # ]: 0 : if (suffix == rf_name.name) {
149 [ # # ]: 0 : param.erase(pos_format);
150 : 0 : return rf_name.rf;
151 : : }
152 : : }
153 : :
154 : : // If no suffix is found, return RESTResponseFormat::UNDEF and original string without query string
155 : 0 : return rf_names[0].rf;
156 : 0 : }
157 : :
158 : 0 : static std::string AvailableDataFormatsString()
159 : : {
160 : 0 : std::string formats;
161 [ # # ]: 0 : for (const auto& rf_name : rf_names) {
162 [ # # ]: 0 : if (strlen(rf_name.name) > 0) {
163 [ # # ]: 0 : formats.append(".");
164 [ # # ]: 0 : formats.append(rf_name.name);
165 [ # # ]: 0 : formats.append(", ");
166 : 0 : }
167 : : }
168 : :
169 [ # # ]: 0 : if (formats.length() > 0)
170 [ # # ]: 0 : return formats.substr(0, formats.length() - 2);
171 : :
172 : 0 : return formats;
173 : 0 : }
174 : :
175 : 0 : static bool CheckWarmup(HTTPRequest* req)
176 : : {
177 : 0 : std::string statusmessage;
178 [ # # ][ # # ]: 0 : if (RPCIsInWarmup(&statusmessage))
179 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
180 : 0 : return true;
181 : 0 : }
182 : :
183 : 0 : static bool rest_headers(const std::any& context,
184 : : HTTPRequest* req,
185 : : const std::string& strURIPart)
186 : : {
187 [ # # ]: 0 : if (!CheckWarmup(req))
188 : 0 : return false;
189 : 0 : std::string param;
190 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
191 [ # # ]: 0 : std::vector<std::string> path = SplitString(param, '/');
192 : :
193 : 0 : std::string raw_count;
194 : 0 : std::string hashStr;
195 [ # # ]: 0 : if (path.size() == 2) {
196 : : // deprecated path: /rest/headers/<count>/<hash>
197 [ # # ]: 0 : hashStr = path[1];
198 [ # # ]: 0 : raw_count = path[0];
199 [ # # ]: 0 : } else if (path.size() == 1) {
200 : : // new path with query parameter: /rest/headers/<hash>?count=<count>
201 [ # # ]: 0 : hashStr = path[0];
202 : : try {
203 [ # # ][ # # ]: 0 : raw_count = req->GetQueryParameter("count").value_or("5");
[ # # ]
204 [ # # ]: 0 : } catch (const std::runtime_error& e) {
205 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, e.what());
206 [ # # ][ # # ]: 0 : }
207 : 0 : } else {
208 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/<hash>.<ext>?count=<count>");
209 : : }
210 : :
211 [ # # ]: 0 : const auto parsed_count{ToIntegral<size_t>(raw_count)};
212 [ # # ][ # # ]: 0 : if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
[ # # ]
213 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
214 : : }
215 : :
216 [ # # ]: 0 : uint256 hash;
217 [ # # ][ # # ]: 0 : if (!ParseHashStr(hashStr, hash))
218 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
219 : :
220 : 0 : const CBlockIndex* tip = nullptr;
221 : 0 : std::vector<const CBlockIndex*> headers;
222 [ # # ]: 0 : headers.reserve(*parsed_count);
223 : : {
224 [ # # ]: 0 : ChainstateManager* maybe_chainman = GetChainman(context, req);
225 [ # # ]: 0 : if (!maybe_chainman) return false;
226 : 0 : ChainstateManager& chainman = *maybe_chainman;
227 [ # # ]: 0 : LOCK(cs_main);
228 [ # # ]: 0 : CChain& active_chain = chainman.ActiveChain();
229 : 0 : tip = active_chain.Tip();
230 [ # # ]: 0 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
231 [ # # ][ # # ]: 0 : while (pindex != nullptr && active_chain.Contains(pindex)) {
[ # # ]
232 [ # # ]: 0 : headers.push_back(pindex);
233 [ # # ]: 0 : if (headers.size() == *parsed_count) {
234 : 0 : break;
235 : : }
236 [ # # ]: 0 : pindex = active_chain.Next(pindex);
237 : : }
238 : 0 : }
239 : :
240 [ # # # # ]: 0 : switch (rf) {
241 : : case RESTResponseFormat::BINARY: {
242 [ # # ]: 0 : DataStream ssHeader{};
243 [ # # ]: 0 : for (const CBlockIndex *pindex : headers) {
244 [ # # ][ # # ]: 0 : ssHeader << pindex->GetBlockHeader();
245 : : }
246 : :
247 [ # # ]: 0 : std::string binaryHeader = ssHeader.str();
248 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
249 [ # # ]: 0 : req->WriteReply(HTTP_OK, binaryHeader);
250 : 0 : return true;
251 : 0 : }
252 : :
253 : : case RESTResponseFormat::HEX: {
254 [ # # ]: 0 : DataStream ssHeader{};
255 [ # # ]: 0 : for (const CBlockIndex *pindex : headers) {
256 [ # # ][ # # ]: 0 : ssHeader << pindex->GetBlockHeader();
257 : : }
258 : :
259 [ # # ][ # # ]: 0 : std::string strHex = HexStr(ssHeader) + "\n";
[ # # ]
260 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
261 [ # # ]: 0 : req->WriteReply(HTTP_OK, strHex);
262 : 0 : return true;
263 : 0 : }
264 : : case RESTResponseFormat::JSON: {
265 [ # # ]: 0 : UniValue jsonHeaders(UniValue::VARR);
266 [ # # ]: 0 : for (const CBlockIndex *pindex : headers) {
267 [ # # ][ # # ]: 0 : jsonHeaders.push_back(blockheaderToJSON(tip, pindex));
268 : : }
269 [ # # ][ # # ]: 0 : std::string strJSON = jsonHeaders.write() + "\n";
270 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
271 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
272 : 0 : return true;
273 : 0 : }
274 : : default: {
275 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
276 : : }
277 : : }
278 : 0 : }
279 : :
280 : 0 : static bool rest_block(const std::any& context,
281 : : HTTPRequest* req,
282 : : const std::string& strURIPart,
283 : : TxVerbosity tx_verbosity)
284 : : {
285 [ # # ]: 0 : if (!CheckWarmup(req))
286 : 0 : return false;
287 : 0 : std::string hashStr;
288 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);
289 : :
290 [ # # ]: 0 : uint256 hash;
291 [ # # ][ # # ]: 0 : if (!ParseHashStr(hashStr, hash))
292 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
293 : :
294 [ # # ]: 0 : CBlock block;
295 : 0 : const CBlockIndex* pblockindex = nullptr;
296 : 0 : const CBlockIndex* tip = nullptr;
297 [ # # ]: 0 : ChainstateManager* maybe_chainman = GetChainman(context, req);
298 [ # # ]: 0 : if (!maybe_chainman) return false;
299 : 0 : ChainstateManager& chainman = *maybe_chainman;
300 : : {
301 [ # # ][ # # ]: 0 : LOCK(cs_main);
302 [ # # ][ # # ]: 0 : tip = chainman.ActiveChain().Tip();
303 [ # # ]: 0 : pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
304 [ # # ]: 0 : if (!pblockindex) {
305 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
306 : : }
307 : :
308 [ # # ][ # # ]: 0 : if (chainman.m_blockman.IsBlockPruned(pblockindex))
309 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
310 : :
311 [ # # ]: 0 : }
312 : :
313 [ # # ][ # # ]: 0 : if (!chainman.m_blockman.ReadBlockFromDisk(block, *pblockindex)) {
314 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
315 : : }
316 : :
317 [ # # # # ]: 0 : switch (rf) {
318 : : case RESTResponseFormat::BINARY: {
319 [ # # ][ # # ]: 0 : CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
320 [ # # ]: 0 : ssBlock << block;
321 [ # # ]: 0 : std::string binaryBlock = ssBlock.str();
322 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
323 [ # # ]: 0 : req->WriteReply(HTTP_OK, binaryBlock);
324 : 0 : return true;
325 : 0 : }
326 : :
327 : : case RESTResponseFormat::HEX: {
328 [ # # ][ # # ]: 0 : CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
329 [ # # ]: 0 : ssBlock << block;
330 [ # # ][ # # ]: 0 : std::string strHex = HexStr(ssBlock) + "\n";
[ # # ]
331 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
332 [ # # ]: 0 : req->WriteReply(HTTP_OK, strHex);
333 : 0 : return true;
334 : 0 : }
335 : :
336 : : case RESTResponseFormat::JSON: {
337 [ # # ]: 0 : UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, tx_verbosity);
338 [ # # ][ # # ]: 0 : std::string strJSON = objBlock.write() + "\n";
339 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
340 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
341 : 0 : return true;
342 : 0 : }
343 : :
344 : : default: {
345 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
346 : : }
347 : : }
348 : 0 : }
349 : :
350 : 0 : static bool rest_block_extended(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
351 : : {
352 : 0 : return rest_block(context, req, strURIPart, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
353 : : }
354 : :
355 : 0 : static bool rest_block_notxdetails(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
356 : : {
357 : 0 : return rest_block(context, req, strURIPart, TxVerbosity::SHOW_TXID);
358 : : }
359 : :
360 : 0 : static bool rest_filter_header(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
361 : : {
362 [ # # ]: 0 : if (!CheckWarmup(req)) return false;
363 : :
364 : 0 : std::string param;
365 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
366 : :
367 [ # # ]: 0 : std::vector<std::string> uri_parts = SplitString(param, '/');
368 : 0 : std::string raw_count;
369 : 0 : std::string raw_blockhash;
370 [ # # ]: 0 : if (uri_parts.size() == 3) {
371 : : // deprecated path: /rest/blockfilterheaders/<filtertype>/<count>/<blockhash>
372 [ # # ]: 0 : raw_blockhash = uri_parts[2];
373 [ # # ]: 0 : raw_count = uri_parts[1];
374 [ # # ]: 0 : } else if (uri_parts.size() == 2) {
375 : : // new path with query parameter: /rest/blockfilterheaders/<filtertype>/<blockhash>?count=<count>
376 [ # # ]: 0 : raw_blockhash = uri_parts[1];
377 : : try {
378 [ # # ][ # # ]: 0 : raw_count = req->GetQueryParameter("count").value_or("5");
[ # # ]
379 [ # # ]: 0 : } catch (const std::runtime_error& e) {
380 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, e.what());
381 [ # # ][ # # ]: 0 : }
382 : 0 : } else {
383 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders/<filtertype>/<blockhash>.<ext>?count=<count>");
384 : : }
385 : :
386 [ # # ]: 0 : const auto parsed_count{ToIntegral<size_t>(raw_count)};
387 [ # # ][ # # ]: 0 : if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) {
[ # # ]
388 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count));
389 : : }
390 : :
391 [ # # ]: 0 : uint256 block_hash;
392 [ # # ][ # # ]: 0 : if (!ParseHashStr(raw_blockhash, block_hash)) {
393 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash);
394 : : }
395 : :
396 : : BlockFilterType filtertype;
397 [ # # ][ # # ]: 0 : if (!BlockFilterTypeByName(uri_parts[0], filtertype)) {
398 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]);
399 : : }
400 : :
401 [ # # ]: 0 : BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
402 [ # # ]: 0 : if (!index) {
403 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
404 : : }
405 : :
406 : 0 : std::vector<const CBlockIndex*> headers;
407 [ # # ]: 0 : headers.reserve(*parsed_count);
408 : : {
409 [ # # ]: 0 : ChainstateManager* maybe_chainman = GetChainman(context, req);
410 [ # # ]: 0 : if (!maybe_chainman) return false;
411 : 0 : ChainstateManager& chainman = *maybe_chainman;
412 [ # # ]: 0 : LOCK(cs_main);
413 [ # # ]: 0 : CChain& active_chain = chainman.ActiveChain();
414 [ # # ]: 0 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block_hash);
415 [ # # ][ # # ]: 0 : while (pindex != nullptr && active_chain.Contains(pindex)) {
[ # # ]
416 [ # # ]: 0 : headers.push_back(pindex);
417 [ # # ]: 0 : if (headers.size() == *parsed_count)
418 : 0 : break;
419 [ # # ]: 0 : pindex = active_chain.Next(pindex);
420 : : }
421 : 0 : }
422 : :
423 [ # # ]: 0 : bool index_ready = index->BlockUntilSyncedToCurrentChain();
424 : :
425 : 0 : std::vector<uint256> filter_headers;
426 [ # # ]: 0 : filter_headers.reserve(*parsed_count);
427 [ # # ]: 0 : for (const CBlockIndex* pindex : headers) {
428 [ # # ]: 0 : uint256 filter_header;
429 [ # # ][ # # ]: 0 : if (!index->LookupFilterHeader(pindex, filter_header)) {
430 [ # # ]: 0 : std::string errmsg = "Filter not found.";
431 : :
432 [ # # ]: 0 : if (!index_ready) {
433 [ # # ]: 0 : errmsg += " Block filters are still in the process of being indexed.";
434 : 0 : } else {
435 [ # # ]: 0 : errmsg += " This error is unexpected and indicates index corruption.";
436 : : }
437 : :
438 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, errmsg);
439 : 0 : }
440 [ # # ]: 0 : filter_headers.push_back(filter_header);
441 : : }
442 : :
443 [ # # # # ]: 0 : switch (rf) {
444 : : case RESTResponseFormat::BINARY: {
445 [ # # ]: 0 : DataStream ssHeader{};
446 [ # # ]: 0 : for (const uint256& header : filter_headers) {
447 [ # # ]: 0 : ssHeader << header;
448 : : }
449 : :
450 [ # # ]: 0 : std::string binaryHeader = ssHeader.str();
451 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
452 [ # # ]: 0 : req->WriteReply(HTTP_OK, binaryHeader);
453 : 0 : return true;
454 : 0 : }
455 : : case RESTResponseFormat::HEX: {
456 [ # # ]: 0 : DataStream ssHeader{};
457 [ # # ]: 0 : for (const uint256& header : filter_headers) {
458 [ # # ]: 0 : ssHeader << header;
459 : : }
460 : :
461 [ # # ][ # # ]: 0 : std::string strHex = HexStr(ssHeader) + "\n";
[ # # ]
462 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
463 [ # # ]: 0 : req->WriteReply(HTTP_OK, strHex);
464 : 0 : return true;
465 : 0 : }
466 : : case RESTResponseFormat::JSON: {
467 [ # # ]: 0 : UniValue jsonHeaders(UniValue::VARR);
468 [ # # ]: 0 : for (const uint256& header : filter_headers) {
469 [ # # ][ # # ]: 0 : jsonHeaders.push_back(header.GetHex());
[ # # ]
470 : : }
471 : :
472 [ # # ][ # # ]: 0 : std::string strJSON = jsonHeaders.write() + "\n";
473 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
474 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
475 : 0 : return true;
476 : 0 : }
477 : : default: {
478 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
479 : : }
480 : : }
481 : 0 : }
482 : :
483 : 0 : static bool rest_block_filter(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
484 : : {
485 [ # # ]: 0 : if (!CheckWarmup(req)) return false;
486 : :
487 : 0 : std::string param;
488 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
489 : :
490 : : // request is sent over URI scheme /rest/blockfilter/filtertype/blockhash
491 [ # # ]: 0 : std::vector<std::string> uri_parts = SplitString(param, '/');
492 [ # # ]: 0 : if (uri_parts.size() != 2) {
493 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilter/<filtertype>/<blockhash>");
494 : : }
495 : :
496 [ # # ]: 0 : uint256 block_hash;
497 [ # # ][ # # ]: 0 : if (!ParseHashStr(uri_parts[1], block_hash)) {
498 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[1]);
499 : : }
500 : :
501 : : BlockFilterType filtertype;
502 [ # # ][ # # ]: 0 : if (!BlockFilterTypeByName(uri_parts[0], filtertype)) {
503 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Unknown filtertype " + uri_parts[0]);
504 : : }
505 : :
506 [ # # ]: 0 : BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
507 [ # # ]: 0 : if (!index) {
508 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]);
509 : : }
510 : :
511 : : const CBlockIndex* block_index;
512 : : bool block_was_connected;
513 : : {
514 [ # # ]: 0 : ChainstateManager* maybe_chainman = GetChainman(context, req);
515 [ # # ]: 0 : if (!maybe_chainman) return false;
516 : 0 : ChainstateManager& chainman = *maybe_chainman;
517 [ # # ]: 0 : LOCK(cs_main);
518 [ # # ]: 0 : block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
519 [ # # ]: 0 : if (!block_index) {
520 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, uri_parts[1] + " not found");
521 : : }
522 [ # # ]: 0 : block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
523 [ # # ]: 0 : }
524 : :
525 [ # # ]: 0 : bool index_ready = index->BlockUntilSyncedToCurrentChain();
526 : :
527 [ # # ]: 0 : BlockFilter filter;
528 [ # # ][ # # ]: 0 : if (!index->LookupFilter(block_index, filter)) {
529 [ # # ]: 0 : std::string errmsg = "Filter not found.";
530 : :
531 [ # # ]: 0 : if (!block_was_connected) {
532 [ # # ]: 0 : errmsg += " Block was not connected to active chain.";
533 [ # # ]: 0 : } else if (!index_ready) {
534 [ # # ]: 0 : errmsg += " Block filters are still in the process of being indexed.";
535 : 0 : } else {
536 [ # # ]: 0 : errmsg += " This error is unexpected and indicates index corruption.";
537 : : }
538 : :
539 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, errmsg);
540 : 0 : }
541 : :
542 [ # # # # ]: 0 : switch (rf) {
543 : : case RESTResponseFormat::BINARY: {
544 [ # # ]: 0 : DataStream ssResp{};
545 [ # # ]: 0 : ssResp << filter;
546 : :
547 [ # # ]: 0 : std::string binaryResp = ssResp.str();
548 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
549 [ # # ]: 0 : req->WriteReply(HTTP_OK, binaryResp);
550 : 0 : return true;
551 : 0 : }
552 : : case RESTResponseFormat::HEX: {
553 [ # # ]: 0 : DataStream ssResp{};
554 [ # # ]: 0 : ssResp << filter;
555 : :
556 [ # # ][ # # ]: 0 : std::string strHex = HexStr(ssResp) + "\n";
[ # # ]
557 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
558 [ # # ]: 0 : req->WriteReply(HTTP_OK, strHex);
559 : 0 : return true;
560 : 0 : }
561 : : case RESTResponseFormat::JSON: {
562 [ # # ]: 0 : UniValue ret(UniValue::VOBJ);
563 [ # # ][ # # ]: 0 : ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
[ # # ][ # # ]
[ # # ][ # # ]
564 [ # # ][ # # ]: 0 : std::string strJSON = ret.write() + "\n";
565 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
566 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
567 : 0 : return true;
568 : 0 : }
569 : : default: {
570 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
571 : : }
572 : : }
573 : 0 : }
574 : :
575 : : // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
576 : : RPCHelpMan getblockchaininfo();
577 : :
578 : 0 : static bool rest_chaininfo(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
579 : : {
580 [ # # ]: 0 : if (!CheckWarmup(req))
581 : 0 : return false;
582 : 0 : std::string param;
583 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
584 : :
585 [ # # ]: 0 : switch (rf) {
586 : : case RESTResponseFormat::JSON: {
587 [ # # ]: 0 : JSONRPCRequest jsonRequest;
588 [ # # ]: 0 : jsonRequest.context = context;
589 [ # # ]: 0 : jsonRequest.params = UniValue(UniValue::VARR);
590 [ # # ][ # # ]: 0 : UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
591 [ # # ][ # # ]: 0 : std::string strJSON = chainInfoObject.write() + "\n";
592 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
593 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
594 : 0 : return true;
595 : 0 : }
596 : : default: {
597 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
598 : : }
599 : : }
600 : 0 : }
601 : :
602 : :
603 : : RPCHelpMan getdeploymentinfo();
604 : :
605 : 0 : static bool rest_deploymentinfo(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
606 : : {
607 [ # # ]: 0 : if (!CheckWarmup(req)) return false;
608 : :
609 : 0 : std::string hash_str;
610 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(hash_str, str_uri_part);
611 : :
612 [ # # ]: 0 : switch (rf) {
613 : : case RESTResponseFormat::JSON: {
614 [ # # ]: 0 : JSONRPCRequest jsonRequest;
615 [ # # ]: 0 : jsonRequest.context = context;
616 [ # # ]: 0 : jsonRequest.params = UniValue(UniValue::VARR);
617 : :
618 [ # # ]: 0 : if (!hash_str.empty()) {
619 [ # # ]: 0 : uint256 hash;
620 [ # # ][ # # ]: 0 : if (!ParseHashStr(hash_str, hash)) {
621 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hash_str);
622 : : }
623 : :
624 [ # # ]: 0 : const ChainstateManager* chainman = GetChainman(context, req);
625 [ # # ]: 0 : if (!chainman) return false;
626 [ # # ][ # # ]: 0 : if (!WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(ParseHashV(hash_str, "blockhash")))) {
[ # # ][ # # ]
[ # # ][ # # ]
627 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Block not found");
628 : : }
629 : :
630 [ # # ][ # # ]: 0 : jsonRequest.params.push_back(hash_str);
631 : 0 : }
632 : :
633 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
634 [ # # ][ # # ]: 0 : req->WriteReply(HTTP_OK, getdeploymentinfo().HandleRequest(jsonRequest).write() + "\n");
[ # # ][ # # ]
[ # # ]
635 : 0 : return true;
636 : 0 : }
637 : : default: {
638 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
639 : : }
640 : : }
641 : :
642 : 0 : }
643 : :
644 : 0 : static bool rest_mempool(const std::any& context, HTTPRequest* req, const std::string& str_uri_part)
645 : : {
646 [ # # ]: 0 : if (!CheckWarmup(req))
647 : 0 : return false;
648 : :
649 : 0 : std::string param;
650 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(param, str_uri_part);
651 [ # # ][ # # ]: 0 : if (param != "contents" && param != "info") {
[ # # ]
652 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/mempool/<info|contents>.json");
653 : : }
654 : :
655 [ # # ]: 0 : const CTxMemPool* mempool = GetMemPool(context, req);
656 [ # # ]: 0 : if (!mempool) return false;
657 : :
658 [ # # ]: 0 : switch (rf) {
659 : : case RESTResponseFormat::JSON: {
660 : 0 : std::string str_json;
661 [ # # ][ # # ]: 0 : if (param == "contents") {
662 : 0 : std::string raw_verbose;
663 : : try {
664 [ # # ][ # # ]: 0 : raw_verbose = req->GetQueryParameter("verbose").value_or("true");
[ # # ]
665 [ # # ]: 0 : } catch (const std::runtime_error& e) {
666 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, e.what());
667 [ # # ][ # # ]: 0 : }
668 [ # # ][ # # ]: 0 : if (raw_verbose != "true" && raw_verbose != "false") {
[ # # ][ # # ]
669 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "The \"verbose\" query parameter must be either \"true\" or \"false\".");
670 : : }
671 : 0 : std::string raw_mempool_sequence;
672 : : try {
673 [ # # ][ # # ]: 0 : raw_mempool_sequence = req->GetQueryParameter("mempool_sequence").value_or("false");
[ # # ]
674 [ # # ]: 0 : } catch (const std::runtime_error& e) {
675 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, e.what());
676 [ # # ][ # # ]: 0 : }
677 [ # # ][ # # ]: 0 : if (raw_mempool_sequence != "true" && raw_mempool_sequence != "false") {
[ # # ][ # # ]
678 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "The \"mempool_sequence\" query parameter must be either \"true\" or \"false\".");
679 : : }
680 [ # # ]: 0 : const bool verbose{raw_verbose == "true"};
681 [ # # ]: 0 : const bool mempool_sequence{raw_mempool_sequence == "true"};
682 [ # # ][ # # ]: 0 : if (verbose && mempool_sequence) {
683 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Verbose results cannot contain mempool sequence values. (hint: set \"verbose=false\")");
684 : : }
685 [ # # ][ # # ]: 0 : str_json = MempoolToJSON(*mempool, verbose, mempool_sequence).write() + "\n";
[ # # ]
686 [ # # ]: 0 : } else {
687 [ # # ][ # # ]: 0 : str_json = MempoolInfoToJSON(*mempool).write() + "\n";
[ # # ]
688 : : }
689 : :
690 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
691 [ # # ]: 0 : req->WriteReply(HTTP_OK, str_json);
692 : 0 : return true;
693 : 0 : }
694 : : default: {
695 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
696 : : }
697 : : }
698 : 0 : }
699 : :
700 : 0 : static bool rest_tx(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
701 : : {
702 [ # # ]: 0 : if (!CheckWarmup(req))
703 : 0 : return false;
704 : 0 : std::string hashStr;
705 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart);
706 : :
707 [ # # ]: 0 : uint256 hash;
708 [ # # ][ # # ]: 0 : if (!ParseHashStr(hashStr, hash))
709 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
710 : :
711 [ # # ]: 0 : if (g_txindex) {
712 [ # # ]: 0 : g_txindex->BlockUntilSyncedToCurrentChain();
713 : 0 : }
714 : :
715 [ # # ]: 0 : const NodeContext* const node = GetNodeContext(context, req);
716 [ # # ]: 0 : if (!node) return false;
717 [ # # ]: 0 : uint256 hashBlock = uint256();
718 [ # # ]: 0 : const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, node->mempool.get(), hash, hashBlock, node->chainman->m_blockman);
719 [ # # ]: 0 : if (!tx) {
720 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
721 : : }
722 : :
723 [ # # # # ]: 0 : switch (rf) {
724 : : case RESTResponseFormat::BINARY: {
725 [ # # ][ # # ]: 0 : CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
726 [ # # ]: 0 : ssTx << tx;
727 : :
728 [ # # ]: 0 : std::string binaryTx = ssTx.str();
729 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
730 [ # # ]: 0 : req->WriteReply(HTTP_OK, binaryTx);
731 : 0 : return true;
732 : 0 : }
733 : :
734 : : case RESTResponseFormat::HEX: {
735 [ # # ][ # # ]: 0 : CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
736 [ # # ]: 0 : ssTx << tx;
737 : :
738 [ # # ][ # # ]: 0 : std::string strHex = HexStr(ssTx) + "\n";
[ # # ]
739 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
740 [ # # ]: 0 : req->WriteReply(HTTP_OK, strHex);
741 : 0 : return true;
742 : 0 : }
743 : :
744 : : case RESTResponseFormat::JSON: {
745 [ # # ]: 0 : UniValue objTx(UniValue::VOBJ);
746 [ # # ]: 0 : TxToUniv(*tx, /*block_hash=*/hashBlock, /*entry=*/ objTx);
747 [ # # ][ # # ]: 0 : std::string strJSON = objTx.write() + "\n";
748 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
749 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
750 : 0 : return true;
751 : 0 : }
752 : :
753 : : default: {
754 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
755 : : }
756 : : }
757 : 0 : }
758 : :
759 : 0 : static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::string& strURIPart)
760 : : {
761 [ # # ]: 0 : if (!CheckWarmup(req))
762 : 0 : return false;
763 : 0 : std::string param;
764 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(param, strURIPart);
765 : :
766 : 0 : std::vector<std::string> uriParts;
767 [ # # ]: 0 : if (param.length() > 1)
768 : : {
769 [ # # ]: 0 : std::string strUriParams = param.substr(1);
770 [ # # ]: 0 : uriParts = SplitString(strUriParams, '/');
771 : 0 : }
772 : :
773 : : // throw exception in case of an empty request
774 [ # # ]: 0 : std::string strRequestMutable = req->ReadBody();
775 [ # # ][ # # ]: 0 : if (strRequestMutable.length() == 0 && uriParts.size() == 0)
776 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
777 : :
778 : 0 : bool fInputParsed = false;
779 : 0 : bool fCheckMemPool = false;
780 : 0 : std::vector<COutPoint> vOutPoints;
781 : :
782 : : // parse/deserialize input
783 : : // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
784 : :
785 [ # # ]: 0 : if (uriParts.size() > 0)
786 : : {
787 : : //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
788 [ # # ][ # # ]: 0 : if (uriParts[0] == "checkmempool") fCheckMemPool = true;
789 : :
790 [ # # ]: 0 : for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
791 : : {
792 [ # # ]: 0 : uint256 txid;
793 : : int32_t nOutput;
794 [ # # ]: 0 : std::string strTxid = uriParts[i].substr(0, uriParts[i].find('-'));
795 [ # # ]: 0 : std::string strOutput = uriParts[i].substr(uriParts[i].find('-')+1);
796 : :
797 [ # # ][ # # ]: 0 : if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
[ # # ]
798 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
799 : :
800 [ # # ]: 0 : txid.SetHex(strTxid);
801 [ # # ][ # # ]: 0 : vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
802 : 0 : }
803 : :
804 [ # # ]: 0 : if (vOutPoints.size() > 0)
805 : 0 : fInputParsed = true;
806 : : else
807 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
808 : 0 : }
809 : :
810 [ # # # # ]: 0 : switch (rf) {
811 : : case RESTResponseFormat::HEX: {
812 : : // convert hex to bin, continue then with bin part
813 [ # # ]: 0 : std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
814 [ # # ]: 0 : strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
815 : : [[fallthrough]];
816 : 0 : }
817 : :
818 : : case RESTResponseFormat::BINARY: {
819 : : try {
820 : : //deserialize only if user sent a request
821 [ # # ]: 0 : if (strRequestMutable.size() > 0)
822 : : {
823 [ # # ]: 0 : if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
824 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed");
825 : :
826 [ # # ]: 0 : DataStream oss{};
827 [ # # ]: 0 : oss << strRequestMutable;
828 [ # # ]: 0 : oss >> fCheckMemPool;
829 [ # # ]: 0 : oss >> vOutPoints;
830 : 0 : }
831 [ # # ]: 0 : } catch (const std::ios_base::failure&) {
832 : : // abort in case of unreadable binary data
833 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Parse error");
834 [ # # ][ # # ]: 0 : }
835 : 0 : break;
836 : : }
837 : :
838 : : case RESTResponseFormat::JSON: {
839 [ # # ]: 0 : if (!fInputParsed)
840 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request");
841 : 0 : break;
842 : : }
843 : : default: {
844 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
845 : : }
846 : : }
847 : :
848 : : // limit max outpoints
849 [ # # ]: 0 : if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
850 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
851 : :
852 : : // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
853 : 0 : std::vector<unsigned char> bitmap;
854 : 0 : std::vector<CCoin> outs;
855 : 0 : std::string bitmapStringRepresentation;
856 : 0 : std::vector<bool> hits;
857 [ # # ]: 0 : bitmap.resize((vOutPoints.size() + 7) / 8);
858 [ # # ]: 0 : ChainstateManager* maybe_chainman = GetChainman(context, req);
859 [ # # ]: 0 : if (!maybe_chainman) return false;
860 : 0 : ChainstateManager& chainman = *maybe_chainman;
861 : : decltype(chainman.ActiveHeight()) active_height;
862 [ # # ]: 0 : uint256 active_hash;
863 : : {
864 : 0 : auto process_utxos = [&vOutPoints, &outs, &hits, &active_height, &active_hash, &chainman](const CCoinsView& view, const CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(chainman.GetMutex()) {
865 [ # # ]: 0 : for (const COutPoint& vOutPoint : vOutPoints) {
866 : 0 : Coin coin;
867 [ # # ][ # # ]: 0 : bool hit = (!mempool || !mempool->isSpent(vOutPoint)) && view.GetCoin(vOutPoint, coin);
[ # # ]
868 [ # # ]: 0 : hits.push_back(hit);
869 [ # # ][ # # ]: 0 : if (hit) outs.emplace_back(std::move(coin));
870 : 0 : }
871 : 0 : active_height = chainman.ActiveHeight();
872 : 0 : active_hash = chainman.ActiveTip()->GetBlockHash();
873 : 0 : };
874 : :
875 [ # # ]: 0 : if (fCheckMemPool) {
876 [ # # ]: 0 : const CTxMemPool* mempool = GetMemPool(context, req);
877 [ # # ]: 0 : if (!mempool) return false;
878 : : // use db+mempool as cache backend in case user likes to query mempool
879 [ # # ][ # # ]: 0 : LOCK2(cs_main, mempool->cs);
880 [ # # ][ # # ]: 0 : CCoinsViewCache& viewChain = chainman.ActiveChainstate().CoinsTip();
881 [ # # ]: 0 : CCoinsViewMemPool viewMempool(&viewChain, *mempool);
882 [ # # ]: 0 : process_utxos(viewMempool, mempool);
883 : 0 : } else {
884 [ # # ]: 0 : LOCK(cs_main);
885 [ # # ][ # # ]: 0 : process_utxos(chainman.ActiveChainstate().CoinsTip(), nullptr);
[ # # ]
886 : 0 : }
887 : :
888 [ # # ]: 0 : for (size_t i = 0; i < hits.size(); ++i) {
889 [ # # ]: 0 : const bool hit = hits[i];
890 [ # # ][ # # ]: 0 : bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output)
891 : 0 : bitmap[i / 8] |= ((uint8_t)hit) << (i % 8);
892 : 0 : }
893 : : }
894 : :
895 [ # # # # ]: 0 : switch (rf) {
896 : : case RESTResponseFormat::BINARY: {
897 : : // serialize data
898 : : // use exact same output as mentioned in Bip64
899 [ # # ]: 0 : DataStream ssGetUTXOResponse{};
900 [ # # ][ # # ]: 0 : ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
[ # # ][ # # ]
901 [ # # ]: 0 : std::string ssGetUTXOResponseString = ssGetUTXOResponse.str();
902 : :
903 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
904 [ # # ]: 0 : req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
905 : 0 : return true;
906 : 0 : }
907 : :
908 : : case RESTResponseFormat::HEX: {
909 [ # # ]: 0 : DataStream ssGetUTXOResponse{};
910 [ # # ][ # # ]: 0 : ssGetUTXOResponse << active_height << active_hash << bitmap << outs;
[ # # ][ # # ]
911 [ # # ][ # # ]: 0 : std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
[ # # ]
912 : :
913 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
914 [ # # ]: 0 : req->WriteReply(HTTP_OK, strHex);
915 : 0 : return true;
916 : 0 : }
917 : :
918 : : case RESTResponseFormat::JSON: {
919 [ # # ]: 0 : UniValue objGetUTXOResponse(UniValue::VOBJ);
920 : :
921 : : // pack in some essentials
922 : : // use more or less the same output as mentioned in Bip64
923 [ # # ][ # # ]: 0 : objGetUTXOResponse.pushKV("chainHeight", active_height);
[ # # ]
924 [ # # ][ # # ]: 0 : objGetUTXOResponse.pushKV("chaintipHash", active_hash.GetHex());
[ # # ][ # # ]
925 [ # # ][ # # ]: 0 : objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation);
[ # # ]
926 : :
927 [ # # ]: 0 : UniValue utxos(UniValue::VARR);
928 [ # # ]: 0 : for (const CCoin& coin : outs) {
929 [ # # ]: 0 : UniValue utxo(UniValue::VOBJ);
930 [ # # ][ # # ]: 0 : utxo.pushKV("height", (int32_t)coin.nHeight);
[ # # ]
931 [ # # ][ # # ]: 0 : utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
[ # # ]
932 : :
933 : : // include the script in a json output
934 [ # # ]: 0 : UniValue o(UniValue::VOBJ);
935 [ # # ]: 0 : ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
936 [ # # ][ # # ]: 0 : utxo.pushKV("scriptPubKey", o);
[ # # ]
937 [ # # ][ # # ]: 0 : utxos.push_back(utxo);
938 : 0 : }
939 [ # # ][ # # ]: 0 : objGetUTXOResponse.pushKV("utxos", utxos);
[ # # ]
940 : :
941 : : // return json string
942 [ # # ][ # # ]: 0 : std::string strJSON = objGetUTXOResponse.write() + "\n";
943 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
944 [ # # ]: 0 : req->WriteReply(HTTP_OK, strJSON);
945 : 0 : return true;
946 : 0 : }
947 : : default: {
948 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
949 : : }
950 : : }
951 : 0 : }
952 : :
953 : 0 : static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
954 : : const std::string& str_uri_part)
955 : : {
956 [ # # ]: 0 : if (!CheckWarmup(req)) return false;
957 : 0 : std::string height_str;
958 [ # # ]: 0 : const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part);
959 : :
960 : 0 : int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
961 [ # # ][ # # ]: 0 : if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
[ # # ]
962 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
[ # # ]
963 : : }
964 : :
965 : 0 : CBlockIndex* pblockindex = nullptr;
966 : : {
967 [ # # ]: 0 : ChainstateManager* maybe_chainman = GetChainman(context, req);
968 [ # # ]: 0 : if (!maybe_chainman) return false;
969 : 0 : ChainstateManager& chainman = *maybe_chainman;
970 [ # # ]: 0 : LOCK(cs_main);
971 [ # # ]: 0 : const CChain& active_chain = chainman.ActiveChain();
972 [ # # ]: 0 : if (blockheight > active_chain.Height()) {
973 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
974 : : }
975 : 0 : pblockindex = active_chain[blockheight];
976 [ # # ]: 0 : }
977 [ # # # # ]: 0 : switch (rf) {
978 : : case RESTResponseFormat::BINARY: {
979 [ # # ]: 0 : DataStream ss_blockhash{};
980 [ # # ]: 0 : ss_blockhash << pblockindex->GetBlockHash();
981 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/octet-stream");
[ # # ]
982 [ # # ][ # # ]: 0 : req->WriteReply(HTTP_OK, ss_blockhash.str());
983 : 0 : return true;
984 : 0 : }
985 : : case RESTResponseFormat::HEX: {
986 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "text/plain");
[ # # ]
987 [ # # ][ # # ]: 0 : req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n");
[ # # ]
988 : 0 : return true;
989 : : }
990 : : case RESTResponseFormat::JSON: {
991 [ # # ][ # # ]: 0 : req->WriteHeader("Content-Type", "application/json");
[ # # ]
992 [ # # ]: 0 : UniValue resp = UniValue(UniValue::VOBJ);
993 [ # # ][ # # ]: 0 : resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex());
[ # # ][ # # ]
994 [ # # ][ # # ]: 0 : req->WriteReply(HTTP_OK, resp.write() + "\n");
[ # # ]
995 : 0 : return true;
996 : 0 : }
997 : : default: {
998 [ # # ][ # # ]: 0 : return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
[ # # ][ # # ]
999 : : }
1000 : : }
1001 : 0 : }
1002 : :
1003 : : static const struct {
1004 : : const char* prefix;
1005 : : bool (*handler)(const std::any& context, HTTPRequest* req, const std::string& strReq);
1006 : : } uri_prefixes[] = {
1007 : : {"/rest/tx/", rest_tx},
1008 : : {"/rest/block/notxdetails/", rest_block_notxdetails},
1009 : : {"/rest/block/", rest_block_extended},
1010 : : {"/rest/blockfilter/", rest_block_filter},
1011 : : {"/rest/blockfilterheaders/", rest_filter_header},
1012 : : {"/rest/chaininfo", rest_chaininfo},
1013 : : {"/rest/mempool/", rest_mempool},
1014 : : {"/rest/headers/", rest_headers},
1015 : : {"/rest/getutxos", rest_getutxos},
1016 : : {"/rest/deploymentinfo/", rest_deploymentinfo},
1017 : : {"/rest/deploymentinfo", rest_deploymentinfo},
1018 : : {"/rest/blockhashbyheight/", rest_blockhash_by_height},
1019 : : };
1020 : :
1021 : 0 : void StartREST(const std::any& context)
1022 : : {
1023 [ # # ]: 0 : for (const auto& up : uri_prefixes) {
1024 : 0 : auto handler = [context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
1025 [ # # ][ # # ]: 0 : RegisterHTTPHandler(up.prefix, false, handler);
[ # # ]
1026 : 0 : }
1027 : 0 : }
1028 : :
1029 : 0 : void InterruptREST()
1030 : : {
1031 : 0 : }
1032 : :
1033 : 0 : void StopREST()
1034 : : {
1035 [ # # ]: 0 : for (const auto& up : uri_prefixes) {
1036 [ # # ][ # # ]: 0 : UnregisterHTTPHandler(up.prefix, false);
1037 : : }
1038 : 0 : }
|