Dash Core  0.12.2.1
P2P Digital Currency
rest.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2014-2015 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 
6 #
7 # Test REST interface
8 #
9 
10 
11 from test_framework.test_framework import BitcoinTestFramework
12 from test_framework.util import *
13 from struct import *
14 from io import BytesIO
15 from codecs import encode
16 import binascii
17 
18 try:
19  import http.client as httplib
20 except ImportError:
21  import httplib
22 try:
23  import urllib.parse as urlparse
24 except ImportError:
25  import urlparse
26 
28  r = 0
29  for i in range(8):
30  t = unpack(b"<I", f.read(4))[0]
31  r += t << (i * 32)
32  return r
33 
34 #allows simple http get calls
35 def http_get_call(host, port, path, response_object = 0):
36  conn = httplib.HTTPConnection(host, port)
37  conn.request('GET', path)
38 
39  if response_object:
40  return conn.getresponse()
41 
42  return conn.getresponse().read().decode('utf-8')
43 
44 #allows simple http post calls with a request body
45 def http_post_call(host, port, path, requestdata = '', response_object = 0):
46  conn = httplib.HTTPConnection(host, port)
47  conn.request('POST', path, requestdata)
48 
49  if response_object:
50  return conn.getresponse()
51 
52  return conn.getresponse().read()
53 
55  FORMAT_SEPARATOR = "."
56 
57  def setup_chain(self):
58  print("Initializing test directory "+self.options.tmpdir)
59  initialize_chain_clean(self.options.tmpdir, 3)
60 
61  def setup_network(self, split=False):
62  self.nodes = start_nodes(3, self.options.tmpdir)
63  connect_nodes_bi(self.nodes,0,1)
64  connect_nodes_bi(self.nodes,1,2)
65  connect_nodes_bi(self.nodes,0,2)
66  self.is_network_split=False
67  self.sync_all()
68 
69  def run_test(self):
70  url = urlparse.urlparse(self.nodes[0].url)
71  print "Mining blocks..."
72 
73  self.nodes[0].generate(1)
74  self.sync_all()
75  self.nodes[2].generate(100)
76  self.sync_all()
77 
78  assert_equal(self.nodes[0].getbalance(), 500)
79 
80  txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
81  self.sync_all()
82  self.nodes[2].generate(1)
83  self.sync_all()
84  bb_hash = self.nodes[0].getbestblockhash()
85 
86  assert_equal(self.nodes[1].getbalance(), Decimal("0.1")) #balance now should be 0.1 on node 1
87 
88  # load the latest 0.1 tx over the REST API
89  json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
90  json_obj = json.loads(json_string)
91  vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
92  # get n of 0.1 outpoint
93  n = 0
94  for vout in json_obj['vout']:
95  if vout['value'] == 0.1:
96  n = vout['n']
97 
98 
99 
102  json_request = '/checkmempool/'+txid+'-'+str(n)
103  json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
104  json_obj = json.loads(json_string)
105 
106  #check chainTip response
107  assert_equal(json_obj['chaintipHash'], bb_hash)
108 
109  #make sure there is one utxo
110  assert_equal(len(json_obj['utxos']), 1)
111  assert_equal(json_obj['utxos'][0]['value'], 0.1)
112 
113 
114 
117  json_request = '/checkmempool/'+vintx+'-0'
118  json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
119  json_obj = json.loads(json_string)
120 
121  #check chainTip response
122  assert_equal(json_obj['chaintipHash'], bb_hash)
123 
124  #make sure there is no utox in the response because this oupoint has been spent
125  assert_equal(len(json_obj['utxos']), 0)
126 
127  #check bitmap
128  assert_equal(json_obj['bitmap'], "0")
129 
130 
131 
134  json_request = '/checkmempool/'+txid+'-'+str(n)+'/'+vintx+'-0'
135  json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
136  json_obj = json.loads(json_string)
137  assert_equal(len(json_obj['utxos']), 1)
138  assert_equal(json_obj['bitmap'], "10")
139 
140  #test binary response
141  bb_hash = self.nodes[0].getbestblockhash()
142 
143  binaryRequest = b'\x01\x02'
144  binaryRequest += hex_str_to_bytes(txid)
145  binaryRequest += pack("i", n)
146  binaryRequest += hex_str_to_bytes(vintx)
147  binaryRequest += pack("i", 0)
148 
149  bin_response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', binaryRequest)
150  output = BytesIO()
151  output.write(bin_response)
152  output.seek(0)
153  chainHeight = unpack("i", output.read(4))[0]
154  hashFromBinResponse = hex(deser_uint256(output))[2:].zfill(65).rstrip("L")
155 
156  assert_equal(bb_hash, hashFromBinResponse) #check if getutxo's chaintip during calculation was fine
157  assert_equal(chainHeight, 102) #chain height must be 102
158 
159 
160 
163 
164  # do a tx and don't sync
165  txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
166  json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
167  json_obj = json.loads(json_string)
168  vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
169  # get n of 0.1 outpoint
170  n = 0
171  for vout in json_obj['vout']:
172  if vout['value'] == 0.1:
173  n = vout['n']
174 
175  json_request = '/'+txid+'-'+str(n)
176  json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
177  json_obj = json.loads(json_string)
178  assert_equal(len(json_obj['utxos']), 0) #there should be a outpoint because it has just added to the mempool
179 
180  json_request = '/checkmempool/'+txid+'-'+str(n)
181  json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
182  json_obj = json.loads(json_string)
183  assert_equal(len(json_obj['utxos']), 1) #there should be a outpoint because it has just added to the mempool
184 
185  #do some invalid requests
186  json_request = '{"checkmempool'
187  response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True)
188  assert_equal(response.status, 500) #must be a 500 because we send a invalid json request
189 
190  json_request = '{"checkmempool'
191  response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', json_request, True)
192  assert_equal(response.status, 500) #must be a 500 because we send a invalid bin request
193 
194  response = http_post_call(url.hostname, url.port, '/rest/getutxos/checkmempool'+self.FORMAT_SEPARATOR+'bin', '', True)
195  assert_equal(response.status, 500) #must be a 500 because we send a invalid bin request
196 
197  #test limits
198  json_request = '/checkmempool/'
199  for x in range(0, 20):
200  json_request += txid+'-'+str(n)+'/'
201  json_request = json_request.rstrip("/")
202  response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
203  assert_equal(response.status, 500) #must be a 500 because we exceeding the limits
204 
205  json_request = '/checkmempool/'
206  for x in range(0, 15):
207  json_request += txid+'-'+str(n)+'/'
208  json_request = json_request.rstrip("/")
209  response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
210  assert_equal(response.status, 200) #must be a 500 because we exceeding the limits
211 
212  self.nodes[0].generate(1) #generate block to not affect upcoming tests
213  self.sync_all()
214 
215 
218 
219  # check binary format
220  response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
221  assert_equal(response.status, 200)
222  assert_greater_than(int(response.getheader('content-length')), 80)
223  response_str = response.read()
224 
225  # compare with block header
226  response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
227  assert_equal(response_header.status, 200)
228  assert_equal(int(response_header.getheader('content-length')), 80)
229  response_header_str = response_header.read()
230  assert_equal(response_str[0:80], response_header_str)
231 
232  # check block hex format
233  response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
234  assert_equal(response_hex.status, 200)
235  assert_greater_than(int(response_hex.getheader('content-length')), 160)
236  response_hex_str = response_hex.read()
237  assert_equal(encode(response_str, "hex_codec")[0:160], response_hex_str[0:160])
238 
239  # compare with hex block header
240  response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
241  assert_equal(response_header_hex.status, 200)
242  assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
243  response_header_hex_str = response_header_hex.read()
244  assert_equal(response_hex_str[0:160], response_header_hex_str[0:160])
245  assert_equal(encode(response_header_str, "hex_codec")[0:160], response_header_hex_str[0:160])
246 
247  # check json format
248  block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
249  block_json_obj = json.loads(block_json_string)
250  assert_equal(block_json_obj['hash'], bb_hash)
251 
252  # compare with json block header
253  response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", True)
254  assert_equal(response_header_json.status, 200)
255  response_header_json_str = response_header_json.read().decode('utf-8')
256  json_obj = json.loads(response_header_json_str, parse_float=Decimal)
257  assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
258  assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same
259 
260  #compare with normal RPC block response
261  rpc_block_json = self.nodes[0].getblock(bb_hash)
262  assert_equal(json_obj[0]['hash'], rpc_block_json['hash'])
263  assert_equal(json_obj[0]['confirmations'], rpc_block_json['confirmations'])
264  assert_equal(json_obj[0]['height'], rpc_block_json['height'])
265  assert_equal(json_obj[0]['version'], rpc_block_json['version'])
266  assert_equal(json_obj[0]['merkleroot'], rpc_block_json['merkleroot'])
267  assert_equal(json_obj[0]['time'], rpc_block_json['time'])
268  assert_equal(json_obj[0]['nonce'], rpc_block_json['nonce'])
269  assert_equal(json_obj[0]['bits'], rpc_block_json['bits'])
270  assert_equal(json_obj[0]['difficulty'], rpc_block_json['difficulty'])
271  assert_equal(json_obj[0]['chainwork'], rpc_block_json['chainwork'])
272  assert_equal(json_obj[0]['previousblockhash'], rpc_block_json['previousblockhash'])
273 
274  #see if we can get 5 headers in one response
275  self.nodes[1].generate(5)
276  self.sync_all()
277  response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/5/'+bb_hash+self.FORMAT_SEPARATOR+"json", True)
278  assert_equal(response_header_json.status, 200)
279  response_header_json_str = response_header_json.read().decode('utf-8')
280  json_obj = json.loads(response_header_json_str)
281  assert_equal(len(json_obj), 5) #now we should have 5 header objects
282 
283  # do tx test
284  tx_hash = block_json_obj['tx'][0]['txid']
285  json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
286  json_obj = json.loads(json_string)
287  assert_equal(json_obj['txid'], tx_hash)
288 
289  # check hex format response
290  hex_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"hex", True)
291  assert_equal(hex_string.status, 200)
292  assert_greater_than(int(response.getheader('content-length')), 10)
293 
294 
295  # check block tx details
296  # let's make 3 tx and mine them on node 1
297  txs = []
298  txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
299  txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
300  txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11))
301  self.sync_all()
302 
303  # check that there are exactly 3 transactions in the TX memory pool before generating the block
304  json_string = http_get_call(url.hostname, url.port, '/rest/mempool/info'+self.FORMAT_SEPARATOR+'json')
305  json_obj = json.loads(json_string)
306  assert_equal(json_obj['size'], 3)
307  # the size of the memory pool should be greater than 3x ~100 bytes
308  assert_greater_than(json_obj['bytes'], 300)
309 
310  # check that there are our submitted transactions in the TX memory pool
311  json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json')
312  json_obj = json.loads(json_string)
313  for tx in txs:
314  assert_equal(tx in json_obj, True)
315 
316  # now mine the transactions
317  newblockhash = self.nodes[1].generate(1)
318  self.sync_all()
319 
320  #check if the 3 tx show up in the new block
321  json_string = http_get_call(url.hostname, url.port, '/rest/block/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
322  json_obj = json.loads(json_string)
323  for tx in json_obj['tx']:
324  if not 'coinbase' in tx['vin'][0]: #exclude coinbase
325  assert_equal(tx['txid'] in txs, True)
326 
327  #check the same but without tx details
328  json_string = http_get_call(url.hostname, url.port, '/rest/block/notxdetails/'+newblockhash[0]+self.FORMAT_SEPARATOR+'json')
329  json_obj = json.loads(json_string)
330  for tx in txs:
331  assert_equal(tx in json_obj['tx'], True)
332 
333  #test rest bestblock
334  bb_hash = self.nodes[0].getbestblockhash()
335 
336  json_string = http_get_call(url.hostname, url.port, '/rest/chaininfo.json')
337  json_obj = json.loads(json_string)
338  assert_equal(json_obj['bestblockhash'], bb_hash)
339 
340 if __name__ == '__main__':
341  RESTTest ().main ()
def setup_network(self, split=False)
Definition: rest.py:61
def hex_str_to_bytes(hex_str)
Definition: util.py:111
def assert_greater_than(thing1, thing2)
Definition: util.py:465
UniValue getbalance(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:792
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None)
Definition: util.py:305
UniValue getblock(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:483
UniValue sendtoaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:409
UniValue getnewaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:113
def initialize_chain_clean(test_dir, num_nodes)
Definition: util.py:252
def http_post_call(host, port, path, requestdata='', response_object=0)
Definition: rest.py:45
def http_get_call(host, port, path, response_object=0)
Definition: rest.py:35
def setup_chain(self)
Definition: rest.py:57
UniValue generate(const UniValue &params, bool fHelp)
Definition: mining.cpp:122
def run_test(self)
Definition: rest.py:69
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
string FORMAT_SEPARATOR
Definition: rest.py:55
is_network_split
Definition: rest.py:66
def assert_equal(thing1, thing2)
Definition: util.py:461
def connect_nodes_bi(nodes, a, b)
Definition: util.py:351
def deser_uint256(f)
Definition: rest.py:27