1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or https://www.opensource.org/licenses/mit-license.php .
6 #include "chainparams.h"
7 #include "primitives/block.h"
8 #include "primitives/transaction.h"
10 #include "httpserver.h"
11 #include "rpc/server.h"
14 #include "txmempool.h"
15 #include "utilstrencodings.h"
18 #include <boost/algorithm/string.hpp>
19 #include <boost/dynamic_bitset.hpp>
25 static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once
45 uint32_t nTxVer; // Don't call this nVersion, that name has a special meaning inside IMPLEMENT_SERIALIZE
49 ADD_SERIALIZE_METHODS;
51 template <typename Stream, typename Operation>
52 inline void SerializationOp(Stream& s, Operation ser_action)
60 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
61 extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
62 extern UniValue mempoolInfoToJSON();
63 extern UniValue mempoolToJSON(bool fVerbose = false);
64 extern UniValue blockheaderToJSON(const CBlockIndex* blockindex);
65 void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex, bool fIncludeAsm=true);
67 static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, string message)
69 req->WriteHeader("Content-Type", "text/plain");
70 req->WriteReply(status, message + "\r\n");
74 static enum RetFormat ParseDataFormat(vector<string>& params, const string& strReq)
76 boost::split(params, strReq, boost::is_any_of("."));
77 if (params.size() > 1) {
78 for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
79 if (params[1] == rf_names[i].name)
80 return rf_names[i].rf;
83 return rf_names[0].rf;
86 static string AvailableDataFormatsString()
89 for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
90 if (strlen(rf_names[i].name) > 0) {
92 formats.append(rf_names[i].name);
96 if (formats.length() > 0)
97 return formats.substr(0, formats.length() - 2);
102 static bool ParseHashStr(const string& strReq, uint256& v)
104 if (!IsHex(strReq) || (strReq.size() != 64))
111 static bool CheckWarmup(HTTPRequest* req)
113 std::string statusmessage;
114 if (RPCIsInWarmup(&statusmessage))
115 return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
119 static bool rest_headers(HTTPRequest* req,
120 const std::string& strURIPart)
122 if (!CheckWarmup(req))
124 vector<string> params;
125 const RetFormat rf = ParseDataFormat(params, strURIPart);
127 boost::split(path, params[0], boost::is_any_of("/"));
129 if (path.size() != 2)
130 return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
132 long count = strtol(path[0].c_str(), NULL, 10);
133 if (count < 1 || count > 2000)
134 return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
136 string hashStr = path[1];
138 if (!ParseHashStr(hashStr, hash))
139 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
141 std::vector<const CBlockIndex *> headers;
142 headers.reserve(count);
145 BlockMap::const_iterator it = mapBlockIndex.find(hash);
146 const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
147 while (pindex != NULL && chainActive.Contains(pindex)) {
148 headers.push_back(pindex);
149 if (headers.size() == (unsigned long)count)
151 pindex = chainActive.Next(pindex);
155 CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
156 BOOST_FOREACH(const CBlockIndex *pindex, headers) {
157 ssHeader << pindex->GetBlockHeader();
162 string binaryHeader = ssHeader.str();
163 req->WriteHeader("Content-Type", "application/octet-stream");
164 req->WriteReply(HTTP_OK, binaryHeader);
169 string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
170 req->WriteHeader("Content-Type", "text/plain");
171 req->WriteReply(HTTP_OK, strHex);
175 UniValue jsonHeaders(UniValue::VARR);
176 BOOST_FOREACH(const CBlockIndex *pindex, headers) {
177 jsonHeaders.push_back(blockheaderToJSON(pindex));
179 string strJSON = jsonHeaders.write() + "\n";
180 req->WriteHeader("Content-Type", "application/json");
181 req->WriteReply(HTTP_OK, strJSON);
185 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
190 return true; // continue to process further HTTP reqs on this cxn
193 static bool rest_block(HTTPRequest* req,
194 const std::string& strURIPart,
197 if (!CheckWarmup(req))
199 vector<string> params;
200 const RetFormat rf = ParseDataFormat(params, strURIPart);
202 string hashStr = params[0];
204 if (!ParseHashStr(hashStr, hash))
205 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
208 CBlockIndex* pblockindex = NULL;
211 if (mapBlockIndex.count(hash) == 0)
212 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
214 pblockindex = mapBlockIndex[hash];
215 if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0)
216 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
218 if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus(), 1))
219 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
222 CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
227 string binaryBlock = ssBlock.str();
228 req->WriteHeader("Content-Type", "application/octet-stream");
229 req->WriteReply(HTTP_OK, binaryBlock);
234 string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
235 req->WriteHeader("Content-Type", "text/plain");
236 req->WriteReply(HTTP_OK, strHex);
241 UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails);
242 string strJSON = objBlock.write() + "\n";
243 req->WriteHeader("Content-Type", "application/json");
244 req->WriteReply(HTTP_OK, strJSON);
249 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
254 return true; // continue to process further HTTP reqs on this cxn
257 static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart)
259 return rest_block(req, strURIPart, true);
262 static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPart)
264 return rest_block(req, strURIPart, false);
267 // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
268 UniValue getblockchaininfo(const UniValue& params, bool fHelp);
270 static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
272 if (!CheckWarmup(req))
274 vector<string> params;
275 const RetFormat rf = ParseDataFormat(params, strURIPart);
279 UniValue rpcParams(UniValue::VARR);
280 UniValue chainInfoObject = getblockchaininfo(rpcParams, false);
281 string strJSON = chainInfoObject.write() + "\n";
282 req->WriteHeader("Content-Type", "application/json");
283 req->WriteReply(HTTP_OK, strJSON);
287 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
292 return true; // continue to process further HTTP reqs on this cxn
295 static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
297 if (!CheckWarmup(req))
299 vector<string> params;
300 const RetFormat rf = ParseDataFormat(params, strURIPart);
304 UniValue mempoolInfoObject = mempoolInfoToJSON();
306 string strJSON = mempoolInfoObject.write() + "\n";
307 req->WriteHeader("Content-Type", "application/json");
308 req->WriteReply(HTTP_OK, strJSON);
312 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
317 return true; // continue to process further HTTP reqs on this cxn
320 static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart)
322 if (!CheckWarmup(req))
324 vector<string> params;
325 const RetFormat rf = ParseDataFormat(params, strURIPart);
329 UniValue mempoolObject = mempoolToJSON(true);
331 string strJSON = mempoolObject.write() + "\n";
332 req->WriteHeader("Content-Type", "application/json");
333 req->WriteReply(HTTP_OK, strJSON);
337 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)");
342 return true; // continue to process further HTTP reqs on this cxn
345 static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
347 if (!CheckWarmup(req))
349 vector<string> params;
350 const RetFormat rf = ParseDataFormat(params, strURIPart);
352 string hashStr = params[0];
354 if (!ParseHashStr(hashStr, hash))
355 return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
358 uint256 hashBlock = uint256();
359 if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
360 return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
362 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
367 string binaryTx = ssTx.str();
368 req->WriteHeader("Content-Type", "application/octet-stream");
369 req->WriteReply(HTTP_OK, binaryTx);
374 string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
375 req->WriteHeader("Content-Type", "text/plain");
376 req->WriteReply(HTTP_OK, strHex);
381 UniValue objTx(UniValue::VOBJ);
382 TxToJSON(tx, hashBlock, objTx);
383 string strJSON = objTx.write() + "\n";
384 req->WriteHeader("Content-Type", "application/json");
385 req->WriteReply(HTTP_OK, strJSON);
390 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
395 return true; // continue to process further HTTP reqs on this cxn
398 static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
400 if (!CheckWarmup(req))
402 vector<string> params;
403 enum RetFormat rf = ParseDataFormat(params, strURIPart);
405 vector<string> uriParts;
406 if (params.size() > 0 && params[0].length() > 1)
408 std::string strUriParams = params[0].substr(1);
409 boost::split(uriParts, strUriParams, boost::is_any_of("/"));
412 // throw exception in case of an empty request
413 std::string strRequestMutable = req->ReadBody();
414 if (strRequestMutable.length() == 0 && uriParts.size() == 0)
415 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request");
417 bool fInputParsed = false;
418 bool fCheckMemPool = false;
419 vector<COutPoint> vOutPoints;
421 // parse/deserialize input
422 // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
424 if (uriParts.size() > 0)
427 //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
428 if (uriParts.size() > 0 && uriParts[0] == "checkmempool")
429 fCheckMemPool = true;
431 for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++)
435 std::string strTxid = uriParts[i].substr(0, uriParts[i].find("-"));
436 std::string strOutput = uriParts[i].substr(uriParts[i].find("-")+1);
438 if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid))
439 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Parse error");
441 txid.SetHex(strTxid);
442 vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput));
445 if (vOutPoints.size() > 0)
448 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request");
453 // convert hex to bin, continue then with bin part
454 std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable);
455 strRequestMutable.assign(strRequestV.begin(), strRequestV.end());
460 //deserialize only if user sent a request
461 if (strRequestMutable.size() > 0)
463 if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA
464 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Combination of URI scheme inputs and raw post data is not allowed");
466 CDataStream oss(SER_NETWORK, PROTOCOL_VERSION);
467 oss << strRequestMutable;
468 oss >> fCheckMemPool;
471 } catch (const std::ios_base::failure& e) {
472 // abort in case of unreadable binary data
473 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Parse error");
480 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request");
484 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
488 // limit max outpoints
489 if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS)
490 return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size()));
492 // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
493 vector<unsigned char> bitmap;
495 std::string bitmapStringRepresentation;
496 boost::dynamic_bitset<unsigned char> hits(vOutPoints.size());
498 LOCK2(cs_main, mempool.cs);
500 CCoinsView viewDummy;
501 CCoinsViewCache view(&viewDummy);
503 CCoinsViewCache& viewChain = *pcoinsTip;
504 CCoinsViewMemPool viewMempool(&viewChain, mempool);
507 view.SetBackend(viewMempool); // switch cache backend to db+mempool in case user likes to query mempool
509 for (size_t i = 0; i < vOutPoints.size(); i++) {
511 uint256 hash = vOutPoints[i].hash;
512 if (view.GetCoins(hash, coins)) {
513 mempool.pruneSpent(hash, coins);
514 if (coins.IsAvailable(vOutPoints[i].n)) {
516 // Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if
517 // n is valid but points to an already spent output (IsNull).
519 coin.nTxVer = coins.nVersion;
520 coin.nHeight = coins.nHeight;
521 coin.out = coins.vout.at(vOutPoints[i].n);
522 assert(!coin.out.IsNull());
523 outs.push_back(coin);
527 bitmapStringRepresentation.append(hits[i] ? "1" : "0"); // form a binary string representation (human-readable for json output)
530 boost::to_block_range(hits, std::back_inserter(bitmap));
535 // use exact same output as mentioned in Bip64
536 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
537 ssGetUTXOResponse << chainActive.Height() << chainActive.LastTip()->GetBlockHash() << bitmap << outs;
538 string ssGetUTXOResponseString = ssGetUTXOResponse.str();
540 req->WriteHeader("Content-Type", "application/octet-stream");
541 req->WriteReply(HTTP_OK, ssGetUTXOResponseString);
546 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
547 ssGetUTXOResponse << chainActive.Height() << chainActive.LastTip()->GetBlockHash() << bitmap << outs;
548 string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
550 req->WriteHeader("Content-Type", "text/plain");
551 req->WriteReply(HTTP_OK, strHex);
556 UniValue objGetUTXOResponse(UniValue::VOBJ);
558 // pack in some essentials
559 // use more or less the same output as mentioned in Bip64
560 objGetUTXOResponse.push_back(Pair("chainHeight", chainActive.Height()));
561 objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.LastTip()->GetBlockHash().GetHex()));
562 objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation));
564 UniValue utxos(UniValue::VARR);
565 BOOST_FOREACH (const CCoin& coin, outs) {
566 UniValue utxo(UniValue::VOBJ);
567 utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer));
568 utxo.push_back(Pair("height", (int32_t)coin.nHeight));
569 utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue)));
571 // include the script in a json output
572 UniValue o(UniValue::VOBJ);
573 ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true);
574 utxo.push_back(Pair("scriptPubKey", o));
575 utxos.push_back(utxo);
577 objGetUTXOResponse.push_back(Pair("utxos", utxos));
579 // return json string
580 string strJSON = objGetUTXOResponse.write() + "\n";
581 req->WriteHeader("Content-Type", "application/json");
582 req->WriteReply(HTTP_OK, strJSON);
586 return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
591 return true; // continue to process further HTTP reqs on this cxn
594 static const struct {
596 bool (*handler)(HTTPRequest* req, const std::string& strReq);
598 {"/rest/tx/", rest_tx},
599 {"/rest/block/notxdetails/", rest_block_notxdetails},
600 {"/rest/block/", rest_block_extended},
601 {"/rest/chaininfo", rest_chaininfo},
602 {"/rest/mempool/info", rest_mempool_info},
603 {"/rest/mempool/contents", rest_mempool_contents},
604 {"/rest/headers/", rest_headers},
605 {"/rest/getutxos", rest_getutxos},
610 for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
611 RegisterHTTPHandler(uri_prefixes[i].prefix, false, uri_prefixes[i].handler);
621 for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++)
622 UnregisterHTTPHandler(uri_prefixes[i].prefix, false);