1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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.
7 #include "chainparams.h"
8 #include "primitives/block.h"
9 #include "primitives/transaction.h"
10 #include "validation.h"
11 #include "httpserver.h"
12 #include "rpc/server.h"
15 #include "txmempool.h"
16 #include "utilstrencodings.h"
19 #include <boost/algorithm/string.hpp>
23 static const size_t MAX_GETUTXOS_OUTPOINTS
= 15; //allow a max of 15 outpoints to be queried at once
43 uint32_t nTxVer
; // Don't call this nVersion, that name has a special meaning inside IMPLEMENT_SERIALIZE
47 ADD_SERIALIZE_METHODS
;
49 template <typename Stream
, typename Operation
>
50 inline void SerializationOp(Stream
& s
, Operation ser_action
)
58 extern void TxToJSON(const CTransaction
& tx
, const uint256 hashBlock
, UniValue
& entry
);
59 extern UniValue
blockToJSON(const CBlock
& block
, const CBlockIndex
* blockindex
, bool txDetails
= false);
60 extern UniValue
mempoolInfoToJSON();
61 extern UniValue
mempoolToJSON(bool fVerbose
= false);
62 extern void ScriptPubKeyToJSON(const CScript
& scriptPubKey
, UniValue
& out
, bool fIncludeHex
);
63 extern UniValue
blockheaderToJSON(const CBlockIndex
* blockindex
);
65 static bool RESTERR(HTTPRequest
* req
, enum HTTPStatusCode status
, std::string message
)
67 req
->WriteHeader("Content-Type", "text/plain");
68 req
->WriteReply(status
, message
+ "\r\n");
72 static enum RetFormat
ParseDataFormat(std::string
& param
, const std::string
& strReq
)
74 const std::string::size_type pos
= strReq
.rfind('.');
75 if (pos
== std::string::npos
)
78 return rf_names
[0].rf
;
81 param
= strReq
.substr(0, pos
);
82 const std::string
suff(strReq
, pos
+ 1);
84 for (unsigned int i
= 0; i
< ARRAYLEN(rf_names
); i
++)
85 if (suff
== rf_names
[i
].name
)
86 return rf_names
[i
].rf
;
88 /* If no suffix is found, return original string. */
90 return rf_names
[0].rf
;
93 static std::string
AvailableDataFormatsString()
95 std::string formats
= "";
96 for (unsigned int i
= 0; i
< ARRAYLEN(rf_names
); i
++)
97 if (strlen(rf_names
[i
].name
) > 0) {
99 formats
.append(rf_names
[i
].name
);
100 formats
.append(", ");
103 if (formats
.length() > 0)
104 return formats
.substr(0, formats
.length() - 2);
109 static bool ParseHashStr(const std::string
& strReq
, uint256
& v
)
111 if (!IsHex(strReq
) || (strReq
.size() != 64))
118 static bool CheckWarmup(HTTPRequest
* req
)
120 std::string statusmessage
;
121 if (RPCIsInWarmup(&statusmessage
))
122 return RESTERR(req
, HTTP_SERVICE_UNAVAILABLE
, "Service temporarily unavailable: " + statusmessage
);
126 static bool rest_headers(HTTPRequest
* req
,
127 const std::string
& strURIPart
)
129 if (!CheckWarmup(req
))
132 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
133 std::vector
<std::string
> path
;
134 boost::split(path
, param
, boost::is_any_of("/"));
136 if (path
.size() != 2)
137 return RESTERR(req
, HTTP_BAD_REQUEST
, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
139 long count
= strtol(path
[0].c_str(), NULL
, 10);
140 if (count
< 1 || count
> 2000)
141 return RESTERR(req
, HTTP_BAD_REQUEST
, "Header count out of range: " + path
[0]);
143 std::string hashStr
= path
[1];
145 if (!ParseHashStr(hashStr
, hash
))
146 return RESTERR(req
, HTTP_BAD_REQUEST
, "Invalid hash: " + hashStr
);
148 std::vector
<const CBlockIndex
*> headers
;
149 headers
.reserve(count
);
152 BlockMap::const_iterator it
= mapBlockIndex
.find(hash
);
153 const CBlockIndex
*pindex
= (it
!= mapBlockIndex
.end()) ? it
->second
: NULL
;
154 while (pindex
!= NULL
&& chainActive
.Contains(pindex
)) {
155 headers
.push_back(pindex
);
156 if (headers
.size() == (unsigned long)count
)
158 pindex
= chainActive
.Next(pindex
);
162 CDataStream
ssHeader(SER_NETWORK
, PROTOCOL_VERSION
);
163 BOOST_FOREACH(const CBlockIndex
*pindex
, headers
) {
164 ssHeader
<< pindex
->GetBlockHeader();
169 std::string binaryHeader
= ssHeader
.str();
170 req
->WriteHeader("Content-Type", "application/octet-stream");
171 req
->WriteReply(HTTP_OK
, binaryHeader
);
176 std::string strHex
= HexStr(ssHeader
.begin(), ssHeader
.end()) + "\n";
177 req
->WriteHeader("Content-Type", "text/plain");
178 req
->WriteReply(HTTP_OK
, strHex
);
182 UniValue
jsonHeaders(UniValue::VARR
);
183 BOOST_FOREACH(const CBlockIndex
*pindex
, headers
) {
184 jsonHeaders
.push_back(blockheaderToJSON(pindex
));
186 std::string strJSON
= jsonHeaders
.write() + "\n";
187 req
->WriteHeader("Content-Type", "application/json");
188 req
->WriteReply(HTTP_OK
, strJSON
);
192 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: .bin, .hex)");
197 return true; // continue to process further HTTP reqs on this cxn
200 static bool rest_block(HTTPRequest
* req
,
201 const std::string
& strURIPart
,
204 if (!CheckWarmup(req
))
207 const RetFormat rf
= ParseDataFormat(hashStr
, strURIPart
);
210 if (!ParseHashStr(hashStr
, hash
))
211 return RESTERR(req
, HTTP_BAD_REQUEST
, "Invalid hash: " + hashStr
);
214 CBlockIndex
* pblockindex
= NULL
;
217 if (mapBlockIndex
.count(hash
) == 0)
218 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not found");
220 pblockindex
= mapBlockIndex
[hash
];
221 if (fHavePruned
&& !(pblockindex
->nStatus
& BLOCK_HAVE_DATA
) && pblockindex
->nTx
> 0)
222 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not available (pruned data)");
224 if (!ReadBlockFromDisk(block
, pblockindex
, Params().GetConsensus()))
225 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not found");
228 CDataStream
ssBlock(SER_NETWORK
, PROTOCOL_VERSION
| RPCSerializationFlags());
233 std::string binaryBlock
= ssBlock
.str();
234 req
->WriteHeader("Content-Type", "application/octet-stream");
235 req
->WriteReply(HTTP_OK
, binaryBlock
);
240 std::string strHex
= HexStr(ssBlock
.begin(), ssBlock
.end()) + "\n";
241 req
->WriteHeader("Content-Type", "text/plain");
242 req
->WriteReply(HTTP_OK
, strHex
);
247 UniValue objBlock
= blockToJSON(block
, pblockindex
, showTxDetails
);
248 std::string strJSON
= objBlock
.write() + "\n";
249 req
->WriteHeader("Content-Type", "application/json");
250 req
->WriteReply(HTTP_OK
, strJSON
);
255 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
260 return true; // continue to process further HTTP reqs on this cxn
263 static bool rest_block_extended(HTTPRequest
* req
, const std::string
& strURIPart
)
265 return rest_block(req
, strURIPart
, true);
268 static bool rest_block_notxdetails(HTTPRequest
* req
, const std::string
& strURIPart
)
270 return rest_block(req
, strURIPart
, false);
273 // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
274 UniValue
getblockchaininfo(const JSONRPCRequest
& request
);
276 static bool rest_chaininfo(HTTPRequest
* req
, const std::string
& strURIPart
)
278 if (!CheckWarmup(req
))
281 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
285 JSONRPCRequest jsonRequest
;
286 jsonRequest
.params
= UniValue(UniValue::VARR
);
287 UniValue chainInfoObject
= getblockchaininfo(jsonRequest
);
288 std::string strJSON
= chainInfoObject
.write() + "\n";
289 req
->WriteHeader("Content-Type", "application/json");
290 req
->WriteReply(HTTP_OK
, strJSON
);
294 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: json)");
299 return true; // continue to process further HTTP reqs on this cxn
302 static bool rest_mempool_info(HTTPRequest
* req
, const std::string
& strURIPart
)
304 if (!CheckWarmup(req
))
307 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
311 UniValue mempoolInfoObject
= mempoolInfoToJSON();
313 std::string strJSON
= mempoolInfoObject
.write() + "\n";
314 req
->WriteHeader("Content-Type", "application/json");
315 req
->WriteReply(HTTP_OK
, strJSON
);
319 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: json)");
324 return true; // continue to process further HTTP reqs on this cxn
327 static bool rest_mempool_contents(HTTPRequest
* req
, const std::string
& strURIPart
)
329 if (!CheckWarmup(req
))
332 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
336 UniValue mempoolObject
= mempoolToJSON(true);
338 std::string strJSON
= mempoolObject
.write() + "\n";
339 req
->WriteHeader("Content-Type", "application/json");
340 req
->WriteReply(HTTP_OK
, strJSON
);
344 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: json)");
349 return true; // continue to process further HTTP reqs on this cxn
352 static bool rest_tx(HTTPRequest
* req
, const std::string
& strURIPart
)
354 if (!CheckWarmup(req
))
357 const RetFormat rf
= ParseDataFormat(hashStr
, strURIPart
);
360 if (!ParseHashStr(hashStr
, hash
))
361 return RESTERR(req
, HTTP_BAD_REQUEST
, "Invalid hash: " + hashStr
);
364 uint256 hashBlock
= uint256();
365 if (!GetTransaction(hash
, tx
, Params().GetConsensus(), hashBlock
, true))
366 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not found");
368 CDataStream
ssTx(SER_NETWORK
, PROTOCOL_VERSION
| RPCSerializationFlags());
373 std::string binaryTx
= ssTx
.str();
374 req
->WriteHeader("Content-Type", "application/octet-stream");
375 req
->WriteReply(HTTP_OK
, binaryTx
);
380 std::string strHex
= HexStr(ssTx
.begin(), ssTx
.end()) + "\n";
381 req
->WriteHeader("Content-Type", "text/plain");
382 req
->WriteReply(HTTP_OK
, strHex
);
387 UniValue
objTx(UniValue::VOBJ
);
388 TxToJSON(*tx
, hashBlock
, objTx
);
389 std::string strJSON
= objTx
.write() + "\n";
390 req
->WriteHeader("Content-Type", "application/json");
391 req
->WriteReply(HTTP_OK
, strJSON
);
396 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
401 return true; // continue to process further HTTP reqs on this cxn
404 static bool rest_getutxos(HTTPRequest
* req
, const std::string
& strURIPart
)
406 if (!CheckWarmup(req
))
409 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
411 std::vector
<std::string
> uriParts
;
412 if (param
.length() > 1)
414 std::string strUriParams
= param
.substr(1);
415 boost::split(uriParts
, strUriParams
, boost::is_any_of("/"));
418 // throw exception in case of a empty request
419 std::string strRequestMutable
= req
->ReadBody();
420 if (strRequestMutable
.length() == 0 && uriParts
.size() == 0)
421 return RESTERR(req
, HTTP_BAD_REQUEST
, "Error: empty request");
423 bool fInputParsed
= false;
424 bool fCheckMemPool
= false;
425 std::vector
<COutPoint
> vOutPoints
;
427 // parse/deserialize input
428 // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
430 if (uriParts
.size() > 0)
433 //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
434 if (uriParts
.size() > 0 && uriParts
[0] == "checkmempool")
435 fCheckMemPool
= true;
437 for (size_t i
= (fCheckMemPool
) ? 1 : 0; i
< uriParts
.size(); i
++)
441 std::string strTxid
= uriParts
[i
].substr(0, uriParts
[i
].find("-"));
442 std::string strOutput
= uriParts
[i
].substr(uriParts
[i
].find("-")+1);
444 if (!ParseInt32(strOutput
, &nOutput
) || !IsHex(strTxid
))
445 return RESTERR(req
, HTTP_BAD_REQUEST
, "Parse error");
447 txid
.SetHex(strTxid
);
448 vOutPoints
.push_back(COutPoint(txid
, (uint32_t)nOutput
));
451 if (vOutPoints
.size() > 0)
454 return RESTERR(req
, HTTP_BAD_REQUEST
, "Error: empty request");
459 // convert hex to bin, continue then with bin part
460 std::vector
<unsigned char> strRequestV
= ParseHex(strRequestMutable
);
461 strRequestMutable
.assign(strRequestV
.begin(), strRequestV
.end());
466 //deserialize only if user sent a request
467 if (strRequestMutable
.size() > 0)
469 if (fInputParsed
) //don't allow sending input over URI and HTTP RAW DATA
470 return RESTERR(req
, HTTP_BAD_REQUEST
, "Combination of URI scheme inputs and raw post data is not allowed");
472 CDataStream
oss(SER_NETWORK
, PROTOCOL_VERSION
);
473 oss
<< strRequestMutable
;
474 oss
>> fCheckMemPool
;
477 } catch (const std::ios_base::failure
& e
) {
478 // abort in case of unreadable binary data
479 return RESTERR(req
, HTTP_BAD_REQUEST
, "Parse error");
486 return RESTERR(req
, HTTP_BAD_REQUEST
, "Error: empty request");
490 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
494 // limit max outpoints
495 if (vOutPoints
.size() > MAX_GETUTXOS_OUTPOINTS
)
496 return RESTERR(req
, HTTP_BAD_REQUEST
, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS
, vOutPoints
.size()));
498 // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
499 std::vector
<unsigned char> bitmap
;
500 std::vector
<CCoin
> outs
;
501 std::string bitmapStringRepresentation
;
502 std::vector
<bool> hits
;
503 bitmap
.resize((vOutPoints
.size() + 7) / 8);
505 LOCK2(cs_main
, mempool
.cs
);
507 CCoinsView viewDummy
;
508 CCoinsViewCache
view(&viewDummy
);
510 CCoinsViewCache
& viewChain
= *pcoinsTip
;
511 CCoinsViewMemPool
viewMempool(&viewChain
, mempool
);
514 view
.SetBackend(viewMempool
); // switch cache backend to db+mempool in case user likes to query mempool
516 for (size_t i
= 0; i
< vOutPoints
.size(); i
++) {
518 uint256 hash
= vOutPoints
[i
].hash
;
520 if (view
.GetCoins(hash
, coins
)) {
521 mempool
.pruneSpent(hash
, coins
);
522 if (coins
.IsAvailable(vOutPoints
[i
].n
)) {
524 // Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if
525 // n is valid but points to an already spent output (IsNull).
527 coin
.nTxVer
= coins
.nVersion
;
528 coin
.nHeight
= coins
.nHeight
;
529 coin
.out
= coins
.vout
.at(vOutPoints
[i
].n
);
530 assert(!coin
.out
.IsNull());
531 outs
.push_back(coin
);
536 bitmapStringRepresentation
.append(hit
? "1" : "0"); // form a binary string representation (human-readable for json output)
537 bitmap
[i
/ 8] |= ((uint8_t)hit
) << (i
% 8);
544 // use exact same output as mentioned in Bip64
545 CDataStream
ssGetUTXOResponse(SER_NETWORK
, PROTOCOL_VERSION
);
546 ssGetUTXOResponse
<< chainActive
.Height() << chainActive
.Tip()->GetBlockHash() << bitmap
<< outs
;
547 std::string ssGetUTXOResponseString
= ssGetUTXOResponse
.str();
549 req
->WriteHeader("Content-Type", "application/octet-stream");
550 req
->WriteReply(HTTP_OK
, ssGetUTXOResponseString
);
555 CDataStream
ssGetUTXOResponse(SER_NETWORK
, PROTOCOL_VERSION
);
556 ssGetUTXOResponse
<< chainActive
.Height() << chainActive
.Tip()->GetBlockHash() << bitmap
<< outs
;
557 std::string strHex
= HexStr(ssGetUTXOResponse
.begin(), ssGetUTXOResponse
.end()) + "\n";
559 req
->WriteHeader("Content-Type", "text/plain");
560 req
->WriteReply(HTTP_OK
, strHex
);
565 UniValue
objGetUTXOResponse(UniValue::VOBJ
);
567 // pack in some essentials
568 // use more or less the same output as mentioned in Bip64
569 objGetUTXOResponse
.push_back(Pair("chainHeight", chainActive
.Height()));
570 objGetUTXOResponse
.push_back(Pair("chaintipHash", chainActive
.Tip()->GetBlockHash().GetHex()));
571 objGetUTXOResponse
.push_back(Pair("bitmap", bitmapStringRepresentation
));
573 UniValue
utxos(UniValue::VARR
);
574 BOOST_FOREACH (const CCoin
& coin
, outs
) {
575 UniValue
utxo(UniValue::VOBJ
);
576 utxo
.push_back(Pair("txvers", (int32_t)coin
.nTxVer
));
577 utxo
.push_back(Pair("height", (int32_t)coin
.nHeight
));
578 utxo
.push_back(Pair("value", ValueFromAmount(coin
.out
.nValue
)));
580 // include the script in a json output
581 UniValue
o(UniValue::VOBJ
);
582 ScriptPubKeyToJSON(coin
.out
.scriptPubKey
, o
, true);
583 utxo
.push_back(Pair("scriptPubKey", o
));
584 utxos
.push_back(utxo
);
586 objGetUTXOResponse
.push_back(Pair("utxos", utxos
));
588 // return json string
589 std::string strJSON
= objGetUTXOResponse
.write() + "\n";
590 req
->WriteHeader("Content-Type", "application/json");
591 req
->WriteReply(HTTP_OK
, strJSON
);
595 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
600 return true; // continue to process further HTTP reqs on this cxn
603 static const struct {
605 bool (*handler
)(HTTPRequest
* req
, const std::string
& strReq
);
607 {"/rest/tx/", rest_tx
},
608 {"/rest/block/notxdetails/", rest_block_notxdetails
},
609 {"/rest/block/", rest_block_extended
},
610 {"/rest/chaininfo", rest_chaininfo
},
611 {"/rest/mempool/info", rest_mempool_info
},
612 {"/rest/mempool/contents", rest_mempool_contents
},
613 {"/rest/headers/", rest_headers
},
614 {"/rest/getutxos", rest_getutxos
},
619 for (unsigned int i
= 0; i
< ARRAYLEN(uri_prefixes
); i
++)
620 RegisterHTTPHandler(uri_prefixes
[i
].prefix
, false, uri_prefixes
[i
].handler
);
630 for (unsigned int i
= 0; i
< ARRAYLEN(uri_prefixes
); i
++)
631 UnregisterHTTPHandler(uri_prefixes
[i
].prefix
, false);