Skip to content

Commit f676c80

Browse files
committed
Add /rest/headers
1 parent 5f7279a commit f676c80

File tree

2 files changed

+99
-14
lines changed

2 files changed

+99
-14
lines changed

qa/rpc-tests/rest.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,67 +23,88 @@
2323
def http_get_call(host, port, path, response_object = 0):
2424
conn = httplib.HTTPConnection(host, port)
2525
conn.request('GET', path)
26-
26+
2727
if response_object:
2828
return conn.getresponse()
29-
29+
3030
return conn.getresponse().read()
3131

3232

3333
class RESTTest (BitcoinTestFramework):
3434
FORMAT_SEPARATOR = "."
35-
35+
3636
def run_test(self):
3737
url = urlparse.urlparse(self.nodes[0].url)
3838
bb_hash = self.nodes[0].getbestblockhash()
39-
39+
4040
# check binary format
4141
response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
4242
assert_equal(response.status, 200)
43-
assert_greater_than(int(response.getheader('content-length')), 10)
44-
43+
assert_greater_than(int(response.getheader('content-length')), 80)
44+
response_str = response.read()
45+
46+
# compare with block header
47+
response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
48+
assert_equal(response_header.status, 200)
49+
assert_equal(int(response_header.getheader('content-length')), 80)
50+
response_header_str = response_header.read()
51+
assert_equal(response_str[0:80], response_header_str)
52+
53+
# check block hex format
54+
response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
55+
assert_equal(response_hex.status, 200)
56+
assert_greater_than(int(response_hex.getheader('content-length')), 160)
57+
response_hex_str = response_hex.read()
58+
assert_equal(response_str.encode("hex")[0:160], response_hex_str[0:160])
59+
60+
# compare with hex block header
61+
response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
62+
assert_equal(response_header_hex.status, 200)
63+
assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
64+
response_header_hex_str = response_header_hex.read()
65+
assert_equal(response_hex_str[0:160], response_header_hex_str[0:160])
66+
assert_equal(response_header_str.encode("hex")[0:160], response_header_hex_str[0:160])
67+
4568
# check json format
4669
json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
4770
json_obj = json.loads(json_string)
4871
assert_equal(json_obj['hash'], bb_hash)
49-
72+
5073
# do tx test
5174
tx_hash = json_obj['tx'][0]['txid'];
5275
json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
5376
json_obj = json.loads(json_string)
5477
assert_equal(json_obj['txid'], tx_hash)
55-
78+
5679
# check hex format response
5780
hex_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"hex", True)
5881
assert_equal(response.status, 200)
5982
assert_greater_than(int(response.getheader('content-length')), 10)
60-
83+
6184
# check block tx details
6285
# let's make 3 tx and mine them on node 1
6386
txs = []
6487
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
6588
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
6689
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
6790
self.sync_all()
68-
91+
6992
# now mine the transactions
7093
newblockhash = self.nodes[1].setgenerate(True, 1)
7194
self.sync_all()
72-
95+
7396
#check if the 3 tx show up in the new block
7497
json_string = http_get_call(url.hostname, url.port, '/rest/block/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
7598
json_obj = json.loads(json_string)
7699
for tx in json_obj['tx']:
77100
if not 'coinbase' in tx['vin'][0]: #exclude coinbase
78101
assert_equal(tx['txid'] in txs, True)
79-
102+
80103
#check the same but without tx details
81104
json_string = http_get_call(url.hostname, url.port, '/rest/block/notxdetails/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
82105
json_obj = json.loads(json_string)
83106
for tx in txs:
84107
assert_equal(tx in json_obj['tx'], True)
85-
86-
87108

88109
if __name__ == '__main__':
89110
RESTTest ().main ()

src/rest.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,69 @@ static bool ParseHashStr(const string& strReq, uint256& v)
8989
return true;
9090
}
9191

92+
static bool rest_headers(AcceptedConnection* conn,
93+
string& strReq,
94+
map<string, string>& mapHeaders,
95+
bool fRun)
96+
{
97+
vector<string> params;
98+
enum RetFormat rf = ParseDataFormat(params, strReq);
99+
vector<string> path;
100+
boost::split(path, params[0], boost::is_any_of("/"));
101+
102+
if (path.size() != 2)
103+
throw RESTERR(HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
104+
105+
long count = strtol(path[0].c_str(), NULL, 10);
106+
if (count < 1 || count > 2000)
107+
throw RESTERR(HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
108+
109+
string hashStr = path[1];
110+
uint256 hash;
111+
if (!ParseHashStr(hashStr, hash))
112+
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
113+
114+
std::vector<CBlockHeader> headers;
115+
headers.reserve(count);
116+
{
117+
LOCK(cs_main);
118+
BlockMap::const_iterator it = mapBlockIndex.find(hash);
119+
const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
120+
while (pindex != NULL && chainActive.Contains(pindex)) {
121+
headers.push_back(pindex->GetBlockHeader());
122+
if (headers.size() == (unsigned long)count)
123+
break;
124+
pindex = chainActive.Next(pindex);
125+
}
126+
}
127+
128+
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
129+
BOOST_FOREACH(const CBlockHeader &header, headers) {
130+
ssHeader << header;
131+
}
132+
133+
switch (rf) {
134+
case RF_BINARY: {
135+
string binaryHeader = ssHeader.str();
136+
conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryHeader.size(), "application/octet-stream") << binaryHeader << std::flush;
137+
return true;
138+
}
139+
140+
case RF_HEX: {
141+
string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
142+
conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
143+
return true;
144+
}
145+
146+
default: {
147+
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
148+
}
149+
}
150+
151+
// not reached
152+
return true; // continue to process further HTTP reqs on this cxn
153+
}
154+
92155
static bool rest_block(AcceptedConnection* conn,
93156
string& strReq,
94157
map<string, string>& mapHeaders,
@@ -224,6 +287,7 @@ static const struct {
224287
{"/rest/tx/", rest_tx},
225288
{"/rest/block/notxdetails/", rest_block_notxdetails},
226289
{"/rest/block/", rest_block_extended},
290+
{"/rest/headers/", rest_headers},
227291
};
228292

229293
bool HTTPReq_REST(AcceptedConnection* conn,

0 commit comments

Comments
 (0)