Merge pull request #5391

932ef50 [REST] JSON output: remove block infos from tx details if it is nested in block (Jonas Schnelli)
cae5486 [REST] added /rest/block/notxdetails/<hash> into REST-interface.md documentation (Jonas Schnelli)
73351c3 [REST] /rest/block response with full tx details (Jonas Schnelli)
This commit is contained in:
Wladimir J. van der Laan 2014-12-11 10:32:36 +01:00
commit 5e521d3e4e
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
4 changed files with 65 additions and 7 deletions

View File

@ -11,12 +11,15 @@ Given a transaction hash,
Returns a transaction, in binary, hex-encoded binary or JSON formats. Returns a transaction, in binary, hex-encoded binary or JSON formats.
`GET /rest/block/BLOCK-HASH.{bin|hex|json}` `GET /rest/block/BLOCK-HASH.{bin|hex|json}`
`GET /rest/block/notxdetails/BLOCK-HASH.{bin|hex|json}`
Given a block hash, Given a block hash,
Returns a block, in binary, hex-encoded binary or JSON formats. Returns a block, in binary, hex-encoded binary or JSON formats.
The HTTP request and response are both handled entirely in-memory, thus making maximum memory usage at least 2.66MB (1 MB max block, plus hex encoding) per request. The HTTP request and response are both handled entirely in-memory, thus making maximum memory usage at least 2.66MB (1 MB max block, plus hex encoding) per request.
With the /notxdetails/ option JSON response will only contain the transaction hash instead of the complete transaction details. The option only affects the JSON response.
For full TX query capability, one must enable the transaction index via "txindex=1" command line / configuration option. For full TX query capability, one must enable the transaction index via "txindex=1" command line / configuration option.
Risks Risks

View File

@ -48,7 +48,7 @@ class RESTTest (BitcoinTestFramework):
assert_equal(json_obj['hash'], bb_hash) assert_equal(json_obj['hash'], bb_hash)
# do tx test # do tx test
tx_hash = json_obj['tx'][0]; tx_hash = json_obj['tx'][0]['txid'];
json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json") json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
json_obj = json.loads(json_string) json_obj = json.loads(json_string)
assert_equal(json_obj['txid'], tx_hash) assert_equal(json_obj['txid'], tx_hash)
@ -58,5 +58,32 @@ class RESTTest (BitcoinTestFramework):
assert_equal(response.status, 200) assert_equal(response.status, 200)
assert_greater_than(int(response.getheader('content-length')), 10) assert_greater_than(int(response.getheader('content-length')), 10)
# check block tx details
# let's make 3 tx and mine them on node 1
txs = []
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
self.sync_all()
# now mine the transactions
newblockhash = self.nodes[1].setgenerate(True, 1)
self.sync_all()
#check if the 3 tx show up in the new block
json_string = http_get_call(url.hostname, url.port, '/rest/block/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
for tx in json_obj['tx']:
if not 'coinbase' in tx['vin'][0]: #exclude coinbase
assert_equal(tx['txid'] in txs, True)
#check the same but without tx details
json_string = http_get_call(url.hostname, url.port, '/rest/block/notxdetails/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
for tx in txs:
assert_equal(tx in json_obj['tx'], True)
if __name__ == '__main__': if __name__ == '__main__':
RESTTest ().main () RESTTest ().main ()

View File

@ -42,7 +42,7 @@ public:
}; };
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry); extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex); extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
static RestErr RESTERR(enum HTTPStatusCode status, string message) static RestErr RESTERR(enum HTTPStatusCode status, string message)
{ {
@ -92,7 +92,8 @@ static bool ParseHashStr(const string& strReq, uint256& v)
static bool rest_block(AcceptedConnection* conn, static bool rest_block(AcceptedConnection* conn,
string& strReq, string& strReq,
map<string, string>& mapHeaders, map<string, string>& mapHeaders,
bool fRun) bool fRun,
bool showTxDetails)
{ {
vector<string> params; vector<string> params;
enum RetFormat rf = ParseDataFormat(params, strReq); enum RetFormat rf = ParseDataFormat(params, strReq);
@ -131,7 +132,7 @@ static bool rest_block(AcceptedConnection* conn,
} }
case RF_JSON: { case RF_JSON: {
Object objBlock = blockToJSON(block, pblockindex); Object objBlock = blockToJSON(block, pblockindex, showTxDetails);
string strJSON = write_string(Value(objBlock), false) + "\n"; string strJSON = write_string(Value(objBlock), false) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true; return true;
@ -146,6 +147,22 @@ static bool rest_block(AcceptedConnection* conn,
return true; // continue to process further HTTP reqs on this cxn return true; // continue to process further HTTP reqs on this cxn
} }
static bool rest_block_extended(AcceptedConnection* conn,
string& strReq,
map<string, string>& mapHeaders,
bool fRun)
{
return rest_block(conn, strReq, mapHeaders, fRun, true);
}
static bool rest_block_notxdetails(AcceptedConnection* conn,
string& strReq,
map<string, string>& mapHeaders,
bool fRun)
{
return rest_block(conn, strReq, mapHeaders, fRun, false);
}
static bool rest_tx(AcceptedConnection* conn, static bool rest_tx(AcceptedConnection* conn,
string& strReq, string& strReq,
map<string, string>& mapHeaders, map<string, string>& mapHeaders,
@ -205,7 +222,8 @@ static const struct {
bool fRun); bool fRun);
} uri_prefixes[] = { } uri_prefixes[] = {
{"/rest/tx/", rest_tx}, {"/rest/tx/", rest_tx},
{"/rest/block/", rest_block}, {"/rest/block/notxdetails/", rest_block_notxdetails},
{"/rest/block/", rest_block_extended},
}; };
bool HTTPReq_REST(AcceptedConnection* conn, bool HTTPReq_REST(AcceptedConnection* conn,

View File

@ -16,6 +16,7 @@
using namespace json_spirit; using namespace json_spirit;
using namespace std; using namespace std;
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex); void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex);
double GetDifficulty(const CBlockIndex* blockindex) double GetDifficulty(const CBlockIndex* blockindex)
@ -50,7 +51,7 @@ double GetDifficulty(const CBlockIndex* blockindex)
} }
Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
{ {
Object result; Object result;
result.push_back(Pair("hash", block.GetHash().GetHex())); result.push_back(Pair("hash", block.GetHash().GetHex()));
@ -65,7 +66,16 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
Array txs; Array txs;
BOOST_FOREACH(const CTransaction&tx, block.vtx) BOOST_FOREACH(const CTransaction&tx, block.vtx)
txs.push_back(tx.GetHash().GetHex()); {
if(txDetails)
{
Object objTx;
TxToJSON(tx, uint256(0), objTx);
txs.push_back(objTx);
}
else
txs.push_back(tx.GetHash().GetHex());
}
result.push_back(Pair("tx", txs)); result.push_back(Pair("tx", txs));
result.push_back(Pair("time", block.GetBlockTime())); result.push_back(Pair("time", block.GetBlockTime()));
result.push_back(Pair("nonce", (uint64_t)block.nNonce)); result.push_back(Pair("nonce", (uint64_t)block.nNonce));