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