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:
commit
5e521d3e4e
@ -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
|
||||||
|
@ -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 ()
|
||||||
|
26
src/rest.cpp
26
src/rest.cpp
@ -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,
|
||||||
|
@ -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)
|
||||||
|
{
|
||||||
|
if(txDetails)
|
||||||
|
{
|
||||||
|
Object objTx;
|
||||||
|
TxToJSON(tx, uint256(0), objTx);
|
||||||
|
txs.push_back(objTx);
|
||||||
|
}
|
||||||
|
else
|
||||||
txs.push_back(tx.GetHash().GetHex());
|
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));
|
||||||
|
Loading…
Reference in New Issue
Block a user