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>
9 #include <primitives/block.h>
10 #include <primitives/transaction.h>
11 #include <validation.h>
12 #include <httpserver.h>
13 #include <rpc/blockchain.h>
14 #include <rpc/server.h>
17 #include <txmempool.h>
18 #include <utilstrencodings.h>
21 #include <boost/algorithm/string.hpp>
25 static const size_t MAX_GETUTXOS_OUTPOINTS
= 15; //allow a max of 15 outpoints to be queried at once
48 ADD_SERIALIZE_METHODS
;
50 CCoin() : nHeight(0) {}
51 explicit CCoin(Coin
&& in
) : nHeight(in
.nHeight
), out(std::move(in
.out
)) {}
53 template <typename Stream
, typename Operation
>
54 inline void SerializationOp(Stream
& s
, Operation ser_action
)
56 uint32_t nTxVerDummy
= 0;
57 READWRITE(nTxVerDummy
);
63 static bool RESTERR(HTTPRequest
* req
, enum HTTPStatusCode status
, std::string message
)
65 req
->WriteHeader("Content-Type", "text/plain");
66 req
->WriteReply(status
, message
+ "\r\n");
70 static enum RetFormat
ParseDataFormat(std::string
& param
, const std::string
& strReq
)
72 const std::string::size_type pos
= strReq
.rfind('.');
73 if (pos
== std::string::npos
)
76 return rf_names
[0].rf
;
79 param
= strReq
.substr(0, pos
);
80 const std::string
suff(strReq
, pos
+ 1);
82 for (unsigned int i
= 0; i
< ARRAYLEN(rf_names
); i
++)
83 if (suff
== rf_names
[i
].name
)
84 return rf_names
[i
].rf
;
86 /* If no suffix is found, return original string. */
88 return rf_names
[0].rf
;
91 static std::string
AvailableDataFormatsString()
93 std::string formats
= "";
94 for (unsigned int i
= 0; i
< ARRAYLEN(rf_names
); i
++)
95 if (strlen(rf_names
[i
].name
) > 0) {
97 formats
.append(rf_names
[i
].name
);
101 if (formats
.length() > 0)
102 return formats
.substr(0, formats
.length() - 2);
107 static bool ParseHashStr(const std::string
& strReq
, uint256
& v
)
109 if (!IsHex(strReq
) || (strReq
.size() != 64))
116 static bool CheckWarmup(HTTPRequest
* req
)
118 std::string statusmessage
;
119 if (RPCIsInWarmup(&statusmessage
))
120 return RESTERR(req
, HTTP_SERVICE_UNAVAILABLE
, "Service temporarily unavailable: " + statusmessage
);
124 static bool rest_headers(HTTPRequest
* req
,
125 const std::string
& strURIPart
)
127 if (!CheckWarmup(req
))
130 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
131 std::vector
<std::string
> path
;
132 boost::split(path
, param
, boost::is_any_of("/"));
134 if (path
.size() != 2)
135 return RESTERR(req
, HTTP_BAD_REQUEST
, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
137 long count
= strtol(path
[0].c_str(), nullptr, 10);
138 if (count
< 1 || count
> 2000)
139 return RESTERR(req
, HTTP_BAD_REQUEST
, "Header count out of range: " + path
[0]);
141 std::string hashStr
= path
[1];
143 if (!ParseHashStr(hashStr
, hash
))
144 return RESTERR(req
, HTTP_BAD_REQUEST
, "Invalid hash: " + hashStr
);
146 std::vector
<const CBlockIndex
*> headers
;
147 headers
.reserve(count
);
150 BlockMap::const_iterator it
= mapBlockIndex
.find(hash
);
151 const CBlockIndex
*pindex
= (it
!= mapBlockIndex
.end()) ? it
->second
: nullptr;
152 while (pindex
!= nullptr && chainActive
.Contains(pindex
)) {
153 headers
.push_back(pindex
);
154 if (headers
.size() == (unsigned long)count
)
156 pindex
= chainActive
.Next(pindex
);
160 CDataStream
ssHeader(SER_NETWORK
, PROTOCOL_VERSION
);
161 for (const CBlockIndex
*pindex
: headers
) {
162 ssHeader
<< pindex
->GetBlockHeader();
167 std::string binaryHeader
= ssHeader
.str();
168 req
->WriteHeader("Content-Type", "application/octet-stream");
169 req
->WriteReply(HTTP_OK
, binaryHeader
);
174 std::string strHex
= HexStr(ssHeader
.begin(), ssHeader
.end()) + "\n";
175 req
->WriteHeader("Content-Type", "text/plain");
176 req
->WriteReply(HTTP_OK
, strHex
);
180 UniValue
jsonHeaders(UniValue::VARR
);
183 for (const CBlockIndex
*pindex
: headers
) {
184 jsonHeaders
.push_back(blockheaderToJSON(pindex
));
187 std::string strJSON
= jsonHeaders
.write() + "\n";
188 req
->WriteHeader("Content-Type", "application/json");
189 req
->WriteReply(HTTP_OK
, strJSON
);
193 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: .bin, .hex)");
198 static bool rest_block(HTTPRequest
* req
,
199 const std::string
& strURIPart
,
202 if (!CheckWarmup(req
))
205 const RetFormat rf
= ParseDataFormat(hashStr
, strURIPart
);
208 if (!ParseHashStr(hashStr
, hash
))
209 return RESTERR(req
, HTTP_BAD_REQUEST
, "Invalid hash: " + hashStr
);
212 CBlockIndex
* pblockindex
= nullptr;
215 if (mapBlockIndex
.count(hash
) == 0)
216 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not found");
218 pblockindex
= mapBlockIndex
[hash
];
219 if (fHavePruned
&& !(pblockindex
->nStatus
& BLOCK_HAVE_DATA
) && pblockindex
->nTx
> 0)
220 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not available (pruned data)");
222 if (!ReadBlockFromDisk(block
, pblockindex
, Params().GetConsensus()))
223 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not found");
226 CDataStream
ssBlock(SER_NETWORK
, PROTOCOL_VERSION
| RPCSerializationFlags());
231 std::string binaryBlock
= ssBlock
.str();
232 req
->WriteHeader("Content-Type", "application/octet-stream");
233 req
->WriteReply(HTTP_OK
, binaryBlock
);
238 std::string strHex
= HexStr(ssBlock
.begin(), ssBlock
.end()) + "\n";
239 req
->WriteHeader("Content-Type", "text/plain");
240 req
->WriteReply(HTTP_OK
, strHex
);
248 objBlock
= blockToJSON(block
, pblockindex
, showTxDetails
);
250 std::string strJSON
= objBlock
.write() + "\n";
251 req
->WriteHeader("Content-Type", "application/json");
252 req
->WriteReply(HTTP_OK
, strJSON
);
257 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
262 static bool rest_block_extended(HTTPRequest
* req
, const std::string
& strURIPart
)
264 return rest_block(req
, strURIPart
, true);
267 static bool rest_block_notxdetails(HTTPRequest
* req
, const std::string
& strURIPart
)
269 return rest_block(req
, strURIPart
, false);
272 // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
273 UniValue
getblockchaininfo(const JSONRPCRequest
& request
);
275 static bool rest_chaininfo(HTTPRequest
* req
, const std::string
& strURIPart
)
277 if (!CheckWarmup(req
))
280 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
284 JSONRPCRequest jsonRequest
;
285 jsonRequest
.params
= UniValue(UniValue::VARR
);
286 UniValue chainInfoObject
= getblockchaininfo(jsonRequest
);
287 std::string strJSON
= chainInfoObject
.write() + "\n";
288 req
->WriteHeader("Content-Type", "application/json");
289 req
->WriteReply(HTTP_OK
, strJSON
);
293 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: json)");
298 static bool rest_mempool_info(HTTPRequest
* req
, const std::string
& strURIPart
)
300 if (!CheckWarmup(req
))
303 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
307 UniValue mempoolInfoObject
= mempoolInfoToJSON();
309 std::string strJSON
= mempoolInfoObject
.write() + "\n";
310 req
->WriteHeader("Content-Type", "application/json");
311 req
->WriteReply(HTTP_OK
, strJSON
);
315 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: json)");
320 static bool rest_mempool_contents(HTTPRequest
* req
, const std::string
& strURIPart
)
322 if (!CheckWarmup(req
))
325 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
329 UniValue mempoolObject
= mempoolToJSON(true);
331 std::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 static bool rest_tx(HTTPRequest
* req
, const std::string
& strURIPart
)
344 if (!CheckWarmup(req
))
347 const RetFormat rf
= ParseDataFormat(hashStr
, strURIPart
);
350 if (!ParseHashStr(hashStr
, hash
))
351 return RESTERR(req
, HTTP_BAD_REQUEST
, "Invalid hash: " + hashStr
);
354 uint256 hashBlock
= uint256();
355 if (!GetTransaction(hash
, tx
, Params().GetConsensus(), hashBlock
, true))
356 return RESTERR(req
, HTTP_NOT_FOUND
, hashStr
+ " not found");
358 CDataStream
ssTx(SER_NETWORK
, PROTOCOL_VERSION
| RPCSerializationFlags());
363 std::string binaryTx
= ssTx
.str();
364 req
->WriteHeader("Content-Type", "application/octet-stream");
365 req
->WriteReply(HTTP_OK
, binaryTx
);
370 std::string strHex
= HexStr(ssTx
.begin(), ssTx
.end()) + "\n";
371 req
->WriteHeader("Content-Type", "text/plain");
372 req
->WriteReply(HTTP_OK
, strHex
);
377 UniValue
objTx(UniValue::VOBJ
);
378 TxToUniv(*tx
, hashBlock
, objTx
);
379 std::string strJSON
= objTx
.write() + "\n";
380 req
->WriteHeader("Content-Type", "application/json");
381 req
->WriteReply(HTTP_OK
, strJSON
);
386 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
391 static bool rest_getutxos(HTTPRequest
* req
, const std::string
& strURIPart
)
393 if (!CheckWarmup(req
))
396 const RetFormat rf
= ParseDataFormat(param
, strURIPart
);
398 std::vector
<std::string
> uriParts
;
399 if (param
.length() > 1)
401 std::string strUriParams
= param
.substr(1);
402 boost::split(uriParts
, strUriParams
, boost::is_any_of("/"));
405 // throw exception in case of an empty request
406 std::string strRequestMutable
= req
->ReadBody();
407 if (strRequestMutable
.length() == 0 && uriParts
.size() == 0)
408 return RESTERR(req
, HTTP_BAD_REQUEST
, "Error: empty request");
410 bool fInputParsed
= false;
411 bool fCheckMemPool
= false;
412 std::vector
<COutPoint
> vOutPoints
;
414 // parse/deserialize input
415 // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ...
417 if (uriParts
.size() > 0)
419 //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...)
420 if (uriParts
[0] == "checkmempool") fCheckMemPool
= true;
422 for (size_t i
= (fCheckMemPool
) ? 1 : 0; i
< uriParts
.size(); i
++)
426 std::string strTxid
= uriParts
[i
].substr(0, uriParts
[i
].find("-"));
427 std::string strOutput
= uriParts
[i
].substr(uriParts
[i
].find("-")+1);
429 if (!ParseInt32(strOutput
, &nOutput
) || !IsHex(strTxid
))
430 return RESTERR(req
, HTTP_BAD_REQUEST
, "Parse error");
432 txid
.SetHex(strTxid
);
433 vOutPoints
.push_back(COutPoint(txid
, (uint32_t)nOutput
));
436 if (vOutPoints
.size() > 0)
439 return RESTERR(req
, HTTP_BAD_REQUEST
, "Error: empty request");
444 // convert hex to bin, continue then with bin part
445 std::vector
<unsigned char> strRequestV
= ParseHex(strRequestMutable
);
446 strRequestMutable
.assign(strRequestV
.begin(), strRequestV
.end());
451 //deserialize only if user sent a request
452 if (strRequestMutable
.size() > 0)
454 if (fInputParsed
) //don't allow sending input over URI and HTTP RAW DATA
455 return RESTERR(req
, HTTP_BAD_REQUEST
, "Combination of URI scheme inputs and raw post data is not allowed");
457 CDataStream
oss(SER_NETWORK
, PROTOCOL_VERSION
);
458 oss
<< strRequestMutable
;
459 oss
>> fCheckMemPool
;
462 } catch (const std::ios_base::failure
& e
) {
463 // abort in case of unreadable binary data
464 return RESTERR(req
, HTTP_BAD_REQUEST
, "Parse error");
471 return RESTERR(req
, HTTP_BAD_REQUEST
, "Error: empty request");
475 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
479 // limit max outpoints
480 if (vOutPoints
.size() > MAX_GETUTXOS_OUTPOINTS
)
481 return RESTERR(req
, HTTP_BAD_REQUEST
, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS
, vOutPoints
.size()));
483 // check spentness and form a bitmap (as well as a JSON capable human-readable string representation)
484 std::vector
<unsigned char> bitmap
;
485 std::vector
<CCoin
> outs
;
486 std::string bitmapStringRepresentation
;
487 std::vector
<bool> hits
;
488 bitmap
.resize((vOutPoints
.size() + 7) / 8);
490 LOCK2(cs_main
, mempool
.cs
);
492 CCoinsView viewDummy
;
493 CCoinsViewCache
view(&viewDummy
);
495 CCoinsViewCache
& viewChain
= *pcoinsTip
;
496 CCoinsViewMemPool
viewMempool(&viewChain
, mempool
);
499 view
.SetBackend(viewMempool
); // switch cache backend to db+mempool in case user likes to query mempool
501 for (size_t i
= 0; i
< vOutPoints
.size(); i
++) {
504 if (view
.GetCoin(vOutPoints
[i
], coin
) && !mempool
.isSpent(vOutPoints
[i
])) {
506 outs
.emplace_back(std::move(coin
));
510 bitmapStringRepresentation
.append(hit
? "1" : "0"); // form a binary string representation (human-readable for json output)
511 bitmap
[i
/ 8] |= ((uint8_t)hit
) << (i
% 8);
518 // use exact same output as mentioned in Bip64
519 CDataStream
ssGetUTXOResponse(SER_NETWORK
, PROTOCOL_VERSION
);
520 ssGetUTXOResponse
<< chainActive
.Height() << chainActive
.Tip()->GetBlockHash() << bitmap
<< outs
;
521 std::string ssGetUTXOResponseString
= ssGetUTXOResponse
.str();
523 req
->WriteHeader("Content-Type", "application/octet-stream");
524 req
->WriteReply(HTTP_OK
, ssGetUTXOResponseString
);
529 CDataStream
ssGetUTXOResponse(SER_NETWORK
, PROTOCOL_VERSION
);
530 ssGetUTXOResponse
<< chainActive
.Height() << chainActive
.Tip()->GetBlockHash() << bitmap
<< outs
;
531 std::string strHex
= HexStr(ssGetUTXOResponse
.begin(), ssGetUTXOResponse
.end()) + "\n";
533 req
->WriteHeader("Content-Type", "text/plain");
534 req
->WriteReply(HTTP_OK
, strHex
);
539 UniValue
objGetUTXOResponse(UniValue::VOBJ
);
541 // pack in some essentials
542 // use more or less the same output as mentioned in Bip64
543 objGetUTXOResponse
.push_back(Pair("chainHeight", chainActive
.Height()));
544 objGetUTXOResponse
.push_back(Pair("chaintipHash", chainActive
.Tip()->GetBlockHash().GetHex()));
545 objGetUTXOResponse
.push_back(Pair("bitmap", bitmapStringRepresentation
));
547 UniValue
utxos(UniValue::VARR
);
548 for (const CCoin
& coin
: outs
) {
549 UniValue
utxo(UniValue::VOBJ
);
550 utxo
.push_back(Pair("height", (int32_t)coin
.nHeight
));
551 utxo
.push_back(Pair("value", ValueFromAmount(coin
.out
.nValue
)));
553 // include the script in a json output
554 UniValue
o(UniValue::VOBJ
);
555 ScriptPubKeyToUniv(coin
.out
.scriptPubKey
, o
, true);
556 utxo
.push_back(Pair("scriptPubKey", o
));
557 utxos
.push_back(utxo
);
559 objGetUTXOResponse
.push_back(Pair("utxos", utxos
));
561 // return json string
562 std::string strJSON
= objGetUTXOResponse
.write() + "\n";
563 req
->WriteHeader("Content-Type", "application/json");
564 req
->WriteReply(HTTP_OK
, strJSON
);
568 return RESTERR(req
, HTTP_NOT_FOUND
, "output format not found (available: " + AvailableDataFormatsString() + ")");
573 static const struct {
575 bool (*handler
)(HTTPRequest
* req
, const std::string
& strReq
);
577 {"/rest/tx/", rest_tx
},
578 {"/rest/block/notxdetails/", rest_block_notxdetails
},
579 {"/rest/block/", rest_block_extended
},
580 {"/rest/chaininfo", rest_chaininfo
},
581 {"/rest/mempool/info", rest_mempool_info
},
582 {"/rest/mempool/contents", rest_mempool_contents
},
583 {"/rest/headers/", rest_headers
},
584 {"/rest/getutxos", rest_getutxos
},
589 for (unsigned int i
= 0; i
< ARRAYLEN(uri_prefixes
); i
++)
590 RegisterHTTPHandler(uri_prefixes
[i
].prefix
, false, uri_prefixes
[i
].handler
);
600 for (unsigned int i
= 0; i
< ARRAYLEN(uri_prefixes
); i
++)
601 UnregisterHTTPHandler(uri_prefixes
[i
].prefix
, false);