From f676c80f437e15ab0ee190e93baea733a88db0f2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 8 Dec 2014 13:44:49 +0100 Subject: [PATCH] Add /rest/headers --- qa/rpc-tests/rest.py | 49 +++++++++++++++++++++++---------- src/rest.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 14 deletions(-) diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index cb1868de36..704d889739 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -23,41 +23,64 @@ except ImportError: def http_get_call(host, port, path, response_object = 0): conn = httplib.HTTPConnection(host, port) conn.request('GET', path) - + if response_object: return conn.getresponse() - + return conn.getresponse().read() class RESTTest (BitcoinTestFramework): FORMAT_SEPARATOR = "." - + def run_test(self): url = urlparse.urlparse(self.nodes[0].url) bb_hash = self.nodes[0].getbestblockhash() - + # check binary format response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True) assert_equal(response.status, 200) - assert_greater_than(int(response.getheader('content-length')), 10) - + assert_greater_than(int(response.getheader('content-length')), 80) + response_str = response.read() + + # compare with block header + response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True) + assert_equal(response_header.status, 200) + assert_equal(int(response_header.getheader('content-length')), 80) + response_header_str = response_header.read() + assert_equal(response_str[0:80], response_header_str) + + # check block hex format + response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True) + assert_equal(response_hex.status, 200) + assert_greater_than(int(response_hex.getheader('content-length')), 160) + response_hex_str = response_hex.read() + assert_equal(response_str.encode("hex")[0:160], response_hex_str[0:160]) + + # compare with hex block header + response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True) + assert_equal(response_header_hex.status, 200) + assert_greater_than(int(response_header_hex.getheader('content-length')), 160) + response_header_hex_str = response_header_hex.read() + assert_equal(response_hex_str[0:160], response_header_hex_str[0:160]) + assert_equal(response_header_str.encode("hex")[0:160], response_header_hex_str[0:160]) + # check json format json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) assert_equal(json_obj['hash'], bb_hash) - + # do tx test 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_obj = json.loads(json_string) assert_equal(json_obj['txid'], tx_hash) - + # check hex format response hex_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"hex", True) assert_equal(response.status, 200) 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 = [] @@ -65,25 +88,23 @@ class RESTTest (BitcoinTestFramework): 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__': RESTTest ().main () diff --git a/src/rest.cpp b/src/rest.cpp index 6329b44c53..c52dbb3cd2 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -89,6 +89,69 @@ static bool ParseHashStr(const string& strReq, uint256& v) return true; } +static bool rest_headers(AcceptedConnection* conn, + string& strReq, + map& mapHeaders, + bool fRun) +{ + vector params; + enum RetFormat rf = ParseDataFormat(params, strReq); + vector path; + boost::split(path, params[0], boost::is_any_of("/")); + + if (path.size() != 2) + throw RESTERR(HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers//.."); + + long count = strtol(path[0].c_str(), NULL, 10); + if (count < 1 || count > 2000) + throw RESTERR(HTTP_BAD_REQUEST, "Header count out of range: " + path[0]); + + string hashStr = path[1]; + uint256 hash; + if (!ParseHashStr(hashStr, hash)) + throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); + + std::vector headers; + headers.reserve(count); + { + LOCK(cs_main); + BlockMap::const_iterator it = mapBlockIndex.find(hash); + const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL; + while (pindex != NULL && chainActive.Contains(pindex)) { + headers.push_back(pindex->GetBlockHeader()); + if (headers.size() == (unsigned long)count) + break; + pindex = chainActive.Next(pindex); + } + } + + CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); + BOOST_FOREACH(const CBlockHeader &header, headers) { + ssHeader << header; + } + + switch (rf) { + case RF_BINARY: { + string binaryHeader = ssHeader.str(); + conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryHeader.size(), "application/octet-stream") << binaryHeader << std::flush; + return true; + } + + case RF_HEX: { + string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n"; + conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush; + return true; + } + + default: { + throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)"); + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn +} + static bool rest_block(AcceptedConnection* conn, string& strReq, map& mapHeaders, @@ -224,6 +287,7 @@ static const struct { {"/rest/tx/", rest_tx}, {"/rest/block/notxdetails/", rest_block_notxdetails}, {"/rest/block/", rest_block_extended}, + {"/rest/headers/", rest_headers}, }; bool HTTPReq_REST(AcceptedConnection* conn,