scripted-diff: Use the C++11 keyword nullptr to denote the pointer literal instead...
[bitcoinplatinum.git] / test / functional / rest.py
bloba69dbb501333a58e3a8372c316fc9023768ae155
1 #!/usr/bin/env python3
2 # Copyright (c) 2014-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.
5 """Test the REST API."""
7 from test_framework.test_framework import BitcoinTestFramework
8 from test_framework.util import *
9 from struct import *
10 from io import BytesIO
11 from codecs import encode
13 import http.client
14 import urllib.parse
16 def deser_uint256(f):
17 r = 0
18 for i in range(8):
19 t = unpack(b"<I", f.read(4))[0]
20 r += t << (i * 32)
21 return r
23 #allows simple http get calls
24 def http_get_call(host, port, path, response_object = 0):
25 conn = http.client.HTTPConnection(host, port)
26 conn.request('GET', path)
28 if response_object:
29 return conn.getresponse()
31 return conn.getresponse().read().decode('utf-8')
33 #allows simple http post calls with a request body
34 def http_post_call(host, port, path, requestdata = '', response_object = 0):
35 conn = http.client.HTTPConnection(host, port)
36 conn.request('POST', path, requestdata)
38 if response_object:
39 return conn.getresponse()
41 return conn.getresponse().read()
43 class RESTTest (BitcoinTestFramework):
44 FORMAT_SEPARATOR = "."
46 def __init__(self):
47 super().__init__()
48 self.setup_clean_chain = True
49 self.num_nodes = 3
51 def setup_network(self, split=False):
52 super().setup_network()
53 connect_nodes_bi(self.nodes, 0, 2)
55 def run_test(self):
56 url = urllib.parse.urlparse(self.nodes[0].url)
57 self.log.info("Mining blocks...")
59 self.nodes[0].generate(1)
60 self.sync_all()
61 self.nodes[2].generate(100)
62 self.sync_all()
64 assert_equal(self.nodes[0].getbalance(), 50)
66 txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
67 self.sync_all()
68 self.nodes[2].generate(1)
69 self.sync_all()
70 bb_hash = self.nodes[0].getbestblockhash()
72 assert_equal(self.nodes[1].getbalance(), Decimal("0.1")) #balance now should be 0.1 on node 1
74 # load the latest 0.1 tx over the REST API
75 json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
76 json_obj = json.loads(json_string)
77 vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
78 # get n of 0.1 outpoint
79 n = 0
80 for vout in json_obj['vout']:
81 if vout['value'] == 0.1:
82 n = vout['n']
85 #######################################
86 # GETUTXOS: query an unspent outpoint #
87 #######################################
88 json_request = '/checkmempool/'+txid+'-'+str(n)
89 json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
90 json_obj = json.loads(json_string)
92 #check chainTip response
93 assert_equal(json_obj['chaintipHash'], bb_hash)
95 #make sure there is one utxo
96 assert_equal(len(json_obj['utxos']), 1)
97 assert_equal(json_obj['utxos'][0]['value'], 0.1)
100 #################################################
101 # GETUTXOS: now query an already spent outpoint #
102 #################################################
103 json_request = '/checkmempool/'+vintx+'-0'
104 json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
105 json_obj = json.loads(json_string)
107 #check chainTip response
108 assert_equal(json_obj['chaintipHash'], bb_hash)
110 #make sure there is no utox in the response because this oupoint has been spent
111 assert_equal(len(json_obj['utxos']), 0)
113 #check bitmap
114 assert_equal(json_obj['bitmap'], "0")
117 ##################################################
118 # GETUTXOS: now check both with the same request #
119 ##################################################
120 json_request = '/checkmempool/'+txid+'-'+str(n)+'/'+vintx+'-0'
121 json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
122 json_obj = json.loads(json_string)
123 assert_equal(len(json_obj['utxos']), 1)
124 assert_equal(json_obj['bitmap'], "10")
126 #test binary response
127 bb_hash = self.nodes[0].getbestblockhash()
129 binaryRequest = b'\x01\x02'
130 binaryRequest += hex_str_to_bytes(txid)
131 binaryRequest += pack("i", n)
132 binaryRequest += hex_str_to_bytes(vintx)
133 binaryRequest += pack("i", 0)
135 bin_response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', binaryRequest)
136 output = BytesIO()
137 output.write(bin_response)
138 output.seek(0)
139 chainHeight = unpack("i", output.read(4))[0]
140 hashFromBinResponse = hex(deser_uint256(output))[2:].zfill(64)
142 assert_equal(bb_hash, hashFromBinResponse) #check if getutxo's chaintip during calculation was fine
143 assert_equal(chainHeight, 102) #chain height must be 102
146 ############################
147 # GETUTXOS: mempool checks #
148 ############################
150 # do a tx and don't sync
151 txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
152 json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
153 json_obj = json.loads(json_string)
154 vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
155 # get n of 0.1 outpoint
156 n = 0
157 for vout in json_obj['vout']:
158 if vout['value'] == 0.1:
159 n = vout['n']
161 json_request = '/'+txid+'-'+str(n)
162 json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
163 json_obj = json.loads(json_string)
164 assert_equal(len(json_obj['utxos']), 0) #there should be an outpoint because it has just added to the mempool
166 json_request = '/checkmempool/'+txid+'-'+str(n)
167 json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
168 json_obj = json.loads(json_string)
169 assert_equal(len(json_obj['utxos']), 1) #there should be an outpoint because it has just added to the mempool
171 #do some invalid requests
172 json_request = '{"checkmempool'
173 response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True)
174 assert_equal(response.status, 400) #must be a 400 because we send an invalid json request
176 json_request = '{"checkmempool'
177 response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', json_request, True)
178 assert_equal(response.status, 400) #must be a 400 because we send an invalid bin request
180 response = http_post_call(url.hostname, url.port, '/rest/getutxos/checkmempool'+self.FORMAT_SEPARATOR+'bin', '', True)
181 assert_equal(response.status, 400) #must be a 400 because we send an invalid bin request
183 #test limits
184 json_request = '/checkmempool/'
185 for x in range(0, 20):
186 json_request += txid+'-'+str(n)+'/'
187 json_request = json_request.rstrip("/")
188 response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
189 assert_equal(response.status, 400) #must be a 400 because we exceeding the limits
191 json_request = '/checkmempool/'
192 for x in range(0, 15):
193 json_request += txid+'-'+str(n)+'/'
194 json_request = json_request.rstrip("/")
195 response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
196 assert_equal(response.status, 200) #must be a 200 because we are within the limits
198 self.nodes[0].generate(1) #generate block to not affect upcoming tests
199 self.sync_all()
201 ################
202 # /rest/block/ #
203 ################
205 # check binary format
206 response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
207 assert_equal(response.status, 200)
208 assert_greater_than(int(response.getheader('content-length')), 80)
209 response_str = response.read()
211 # compare with block header
212 response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
213 assert_equal(response_header.status, 200)
214 assert_equal(int(response_header.getheader('content-length')), 80)
215 response_header_str = response_header.read()
216 assert_equal(response_str[0:80], response_header_str)
218 # check block hex format
219 response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
220 assert_equal(response_hex.status, 200)
221 assert_greater_than(int(response_hex.getheader('content-length')), 160)
222 response_hex_str = response_hex.read()
223 assert_equal(encode(response_str, "hex_codec")[0:160], response_hex_str[0:160])
225 # compare with hex block header
226 response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
227 assert_equal(response_header_hex.status, 200)
228 assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
229 response_header_hex_str = response_header_hex.read()
230 assert_equal(response_hex_str[0:160], response_header_hex_str[0:160])
231 assert_equal(encode(response_header_str, "hex_codec")[0:160], response_header_hex_str[0:160])
233 # check json format
234 block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
235 block_json_obj = json.loads(block_json_string)
236 assert_equal(block_json_obj['hash'], bb_hash)
238 # compare with json block header
239 response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", True)
240 assert_equal(response_header_json.status, 200)
241 response_header_json_str = response_header_json.read().decode('utf-8')
242 json_obj = json.loads(response_header_json_str, parse_float=Decimal)
243 assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
244 assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same
246 #compare with normal RPC block response
247 rpc_block_json = self.nodes[0].getblock(bb_hash)
248 assert_equal(json_obj[0]['hash'], rpc_block_json['hash'])
249 assert_equal(json_obj[0]['confirmations'], rpc_block_json['confirmations'])
250 assert_equal(json_obj[0]['height'], rpc_block_json['height'])
251 assert_equal(json_obj[0]['version'], rpc_block_json['version'])
252 assert_equal(json_obj[0]['merkleroot'], rpc_block_json['merkleroot'])
253 assert_equal(json_obj[0]['time'], rpc_block_json['time'])
254 assert_equal(json_obj[0]['nonce'], rpc_block_json['nonce'])
255 assert_equal(json_obj[0]['bits'], rpc_block_json['bits'])
256 assert_equal(json_obj[0]['difficulty'], rpc_block_json['difficulty'])
257 assert_equal(json_obj[0]['chainwork'], rpc_block_json['chainwork'])
258 assert_equal(json_obj[0]['previousblockhash'], rpc_block_json['previousblockhash'])
260 #see if we can get 5 headers in one response
261 self.nodes[1].generate(5)
262 self.sync_all()
263 response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/5/'+bb_hash+self.FORMAT_SEPARATOR+"json", True)
264 assert_equal(response_header_json.status, 200)
265 response_header_json_str = response_header_json.read().decode('utf-8')
266 json_obj = json.loads(response_header_json_str)
267 assert_equal(len(json_obj), 5) #now we should have 5 header objects
269 # do tx test
270 tx_hash = block_json_obj['tx'][0]['txid']
271 json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
272 json_obj = json.loads(json_string)
273 assert_equal(json_obj['txid'], tx_hash)
275 # check hex format response
276 hex_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"hex", True)
277 assert_equal(hex_string.status, 200)
278 assert_greater_than(int(response.getheader('content-length')), 10)
281 # check block tx details
282 # let's make 3 tx and mine them on node 1
283 txs = []
284 txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
285 txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
286 txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
287 self.sync_all()
289 # check that there are exactly 3 transactions in the TX memory pool before generating the block
290 json_string = http_get_call(url.hostname, url.port, '/rest/mempool/info'+self.FORMAT_SEPARATOR+'json')
291 json_obj = json.loads(json_string)
292 assert_equal(json_obj['size'], 3)
293 # the size of the memory pool should be greater than 3x ~100 bytes
294 assert_greater_than(json_obj['bytes'], 300)
296 # check that there are our submitted transactions in the TX memory pool
297 json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json')
298 json_obj = json.loads(json_string)
299 for tx in txs:
300 assert_equal(tx in json_obj, True)
302 # now mine the transactions
303 newblockhash = self.nodes[1].generate(1)
304 self.sync_all()
306 #check if the 3 tx show up in the new block
307 json_string = http_get_call(url.hostname, url.port, '/rest/block/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
308 json_obj = json.loads(json_string)
309 for tx in json_obj['tx']:
310 if not 'coinbase' in tx['vin'][0]: #exclude coinbase
311 assert_equal(tx['txid'] in txs, True)
313 #check the same but without tx details
314 json_string = http_get_call(url.hostname, url.port, '/rest/block/notxdetails/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
315 json_obj = json.loads(json_string)
316 for tx in txs:
317 assert_equal(tx in json_obj['tx'], True)
319 #test rest bestblock
320 bb_hash = self.nodes[0].getbestblockhash()
322 json_string = http_get_call(url.hostname, url.port, '/rest/chaininfo.json')
323 json_obj = json.loads(json_string)
324 assert_equal(json_obj['bestblockhash'], bb_hash)
326 if __name__ == '__main__':
327 RESTTest ().main ()