99
1010from test_framework import BitcoinTestFramework
1111from util import *
12+ from struct import *
13+ import binascii
1214import json
15+ import StringIO
1316
1417try :
1518 import http .client as httplib
2023except ImportError :
2124 import urlparse
2225
23- def http_get_call (host , port , path , response_object = 0 ):
26+ def deser_uint256 (f ):
27+ r = 0
28+ for i in range (8 ):
29+ t = unpack (b"<I" , f .read (4 ))[0 ]
30+ r += t << (i * 32 )
31+ return r
32+
33+ #allows simple http get calls with a request body
34+ def http_get_call (host , port , path , requestdata = '' , response_object = 0 ):
2435 conn = httplib .HTTPConnection (host , port )
25- conn .request ('GET' , path )
36+ conn .request ('GET' , path , requestdata )
2637
2738 if response_object :
2839 return conn .getresponse ()
2940
3041 return conn .getresponse ().read ()
3142
32-
3343class RESTTest (BitcoinTestFramework ):
3444 FORMAT_SEPARATOR = "."
3545
46+ def setup_chain (self ):
47+ print ("Initializing test directory " + self .options .tmpdir )
48+ initialize_chain_clean (self .options .tmpdir , 3 )
49+
50+ def setup_network (self , split = False ):
51+ self .nodes = start_nodes (3 , self .options .tmpdir )
52+ connect_nodes_bi (self .nodes ,0 ,1 )
53+ connect_nodes_bi (self .nodes ,1 ,2 )
54+ connect_nodes_bi (self .nodes ,0 ,2 )
55+ self .is_network_split = False
56+ self .sync_all ()
57+
3658 def run_test (self ):
3759 url = urlparse .urlparse (self .nodes [0 ].url )
60+ print "Mining blocks..."
61+
62+ self .nodes [0 ].setgenerate (True , 1 )
63+ self .sync_all ()
64+ self .nodes [2 ].setgenerate (True , 100 )
65+ self .sync_all ()
66+
67+ assert_equal (self .nodes [0 ].getbalance (), 50 )
68+
69+ txid = self .nodes [0 ].sendtoaddress (self .nodes [1 ].getnewaddress (), 0.1 )
70+ self .sync_all ()
71+ self .nodes [2 ].setgenerate (True , 1 )
72+ self .sync_all ()
73+ bb_hash = self .nodes [0 ].getbestblockhash ()
74+
75+ assert_equal (self .nodes [1 ].getbalance (), Decimal ("0.1" )) #balance now should be 0.1 on node 1
76+
77+ # load the latest 0.1 tx over the REST API
78+ json_string = http_get_call (url .hostname , url .port , '/rest/tx/' + txid + self .FORMAT_SEPARATOR + "json" )
79+ json_obj = json .loads (json_string )
80+ vintx = json_obj ['vin' ][0 ]['txid' ] # get the vin to later check for utxo (should be spent by then)
81+ # get n of 0.1 outpoint
82+ n = 0
83+ for vout in json_obj ['vout' ]:
84+ if vout ['value' ] == 0.1 :
85+ n = vout ['n' ]
86+
87+
88+ ######################################
89+ # GETUTXOS: query a unspent outpoint #
90+ ######################################
91+ json_request = '{"checkmempool":true,"outpoints":[{"txid":"' + txid + '","n":' + str (n )+ '}]}'
92+ json_string = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request )
93+ json_obj = json .loads (json_string )
94+
95+ #check chainTip response
96+ assert_equal (json_obj ['chaintipHash' ], bb_hash )
97+
98+ #make sure there is one utxo
99+ assert_equal (len (json_obj ['utxos' ]), 1 )
100+ assert_equal (json_obj ['utxos' ][0 ]['value' ], 0.1 )
101+
102+
103+ ################################################
104+ # GETUTXOS: now query a already spent outpoint #
105+ ################################################
106+ json_request = '{"checkmempool":true,"outpoints":[{"txid":"' + vintx + '","n":0}]}'
107+ json_string = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request )
108+ json_obj = json .loads (json_string )
109+
110+ #check chainTip response
111+ assert_equal (json_obj ['chaintipHash' ], bb_hash )
112+
113+ #make sure there is no utox in the response because this oupoint has been spent
114+ assert_equal (len (json_obj ['utxos' ]), 0 )
115+
116+ #check bitmap
117+ assert_equal (json_obj ['bitmap' ], "0" )
118+
119+
120+ ##################################################
121+ # GETUTXOS: now check both with the same request #
122+ ##################################################
123+ json_request = '{"checkmempool":true,"outpoints":[{"txid":"' + txid + '","n":' + str (n )+ '},{"txid":"' + vintx + '","n":0}]}'
124+ json_string = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request )
125+ json_obj = json .loads (json_string )
126+ assert_equal (len (json_obj ['utxos' ]), 1 )
127+ assert_equal (json_obj ['bitmap' ], "10" )
128+
129+ #test binary response
38130 bb_hash = self .nodes [0 ].getbestblockhash ()
39131
132+ binaryRequest = b'\x01 \x02 '
133+ binaryRequest += binascii .unhexlify (txid )
134+ binaryRequest += pack ("i" , n );
135+ binaryRequest += binascii .unhexlify (vintx );
136+ binaryRequest += pack ("i" , 0 );
137+
138+ bin_response = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'bin' , binaryRequest )
139+
140+ output = StringIO .StringIO ()
141+ output .write (bin_response )
142+ output .seek (0 )
143+ chainHeight = unpack ("i" , output .read (4 ))[0 ]
144+ hashFromBinResponse = hex (deser_uint256 (output ))[2 :].zfill (65 ).rstrip ("L" )
145+
146+ assert_equal (bb_hash , hashFromBinResponse ) #check if getutxo's chaintip during calculation was fine
147+ assert_equal (chainHeight , 102 ) #chain height must be 102
148+
149+
150+ ############################
151+ # GETUTXOS: mempool checks #
152+ ############################
153+
154+ # do a tx and don't sync
155+ txid = self .nodes [0 ].sendtoaddress (self .nodes [1 ].getnewaddress (), 0.1 )
156+ json_string = http_get_call (url .hostname , url .port , '/rest/tx/' + txid + self .FORMAT_SEPARATOR + "json" )
157+ json_obj = json .loads (json_string )
158+ vintx = json_obj ['vin' ][0 ]['txid' ] # get the vin to later check for utxo (should be spent by then)
159+ # get n of 0.1 outpoint
160+ n = 0
161+ for vout in json_obj ['vout' ]:
162+ if vout ['value' ] == 0.1 :
163+ n = vout ['n' ]
164+
165+ json_request = '{"checkmempool":false,"outpoints":[{"txid":"' + txid + '","n":' + str (n )+ '}]}'
166+ json_string = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request )
167+ json_obj = json .loads (json_string )
168+ assert_equal (len (json_obj ['utxos' ]), 0 ) #there should be a outpoint because it has just added to the mempool
169+
170+ json_request = '{"checkmempool":true,"outpoints":[{"txid":"' + txid + '","n":' + str (n )+ '}]}'
171+ json_string = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request )
172+ json_obj = json .loads (json_string )
173+ assert_equal (len (json_obj ['utxos' ]), 1 ) #there should be a outpoint because it has just added to the mempool
174+
175+ #do some invalid requests
176+ json_request = '{"checkmempool'
177+ response = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request , True )
178+ assert_equal (response .status , 500 ) #must be a 500 because we send a invalid json request
179+
180+ json_request = '{"checkmempool'
181+ response = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'bin' , json_request , True )
182+ assert_equal (response .status , 500 ) #must be a 500 because we send a invalid bin request
183+
184+ #test limits
185+ json_request = '{"checkmempool":true,"outpoints":['
186+ for x in range (0 , 200 ):
187+ json_request += '{"txid":"' + txid + '","n":' + str (n )+ '},'
188+ json_request = json_request .rstrip ("," )
189+ json_request += "]}" ;
190+ response = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request , True )
191+ assert_equal (response .status , 500 ) #must be a 500 because we exceeding the limits
192+
193+ json_request = '{"checkmempool":true,"outpoints":['
194+ for x in range (0 , 90 ):
195+ json_request += '{"txid":"' + txid + '","n":' + str (n )+ '},'
196+ json_request = json_request .rstrip ("," )
197+ json_request += "]}" ;
198+ response = http_get_call (url .hostname , url .port , '/rest/getutxos' + self .FORMAT_SEPARATOR + 'json' , json_request , True )
199+ assert_equal (response .status , 200 ) #must be a 500 because we exceeding the limits
200+
201+ self .nodes [0 ].setgenerate (True , 1 ) #generate block to not affect upcomming tests
202+ self .sync_all ()
203+
204+ ################
205+ # /rest/block/ #
206+ ################
207+
40208 # check binary format
41- response = http_get_call (url .hostname , url .port , '/rest/block/' + bb_hash + self .FORMAT_SEPARATOR + "bin" , True )
209+ response = http_get_call (url .hostname , url .port , '/rest/block/' + bb_hash + self .FORMAT_SEPARATOR + "bin" , "" , True )
42210 assert_equal (response .status , 200 )
43211 assert_greater_than (int (response .getheader ('content-length' )), 80 )
44212 response_str = response .read ()
45213
46214 # compare with block header
47- response_header = http_get_call (url .hostname , url .port , '/rest/headers/1/' + bb_hash + self .FORMAT_SEPARATOR + "bin" , True )
215+ response_header = http_get_call (url .hostname , url .port , '/rest/headers/1/' + bb_hash + self .FORMAT_SEPARATOR + "bin" , "" , True )
48216 assert_equal (response_header .status , 200 )
49217 assert_equal (int (response_header .getheader ('content-length' )), 80 )
50218 response_header_str = response_header .read ()
51219 assert_equal (response_str [0 :80 ], response_header_str )
52220
53221 # check block hex format
54- response_hex = http_get_call (url .hostname , url .port , '/rest/block/' + bb_hash + self .FORMAT_SEPARATOR + "hex" , True )
222+ response_hex = http_get_call (url .hostname , url .port , '/rest/block/' + bb_hash + self .FORMAT_SEPARATOR + "hex" , "" , True )
55223 assert_equal (response_hex .status , 200 )
56224 assert_greater_than (int (response_hex .getheader ('content-length' )), 160 )
57225 response_hex_str = response_hex .read ()
58226 assert_equal (response_str .encode ("hex" )[0 :160 ], response_hex_str [0 :160 ])
59227
60228 # 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 )
229+ response_header_hex = http_get_call (url .hostname , url .port , '/rest/headers/1/' + bb_hash + self .FORMAT_SEPARATOR + "hex" , "" , True )
62230 assert_equal (response_header_hex .status , 200 )
63231 assert_greater_than (int (response_header_hex .getheader ('content-length' )), 160 )
64232 response_header_hex_str = response_header_hex .read ()
@@ -77,9 +245,11 @@ def run_test(self):
77245 assert_equal (json_obj ['txid' ], tx_hash )
78246
79247 # check hex format response
80- hex_string = http_get_call (url .hostname , url .port , '/rest/tx/' + tx_hash + self .FORMAT_SEPARATOR + "hex" , True )
248+ hex_string = http_get_call (url .hostname , url .port , '/rest/tx/' + tx_hash + self .FORMAT_SEPARATOR + "hex" , "" , True )
81249 assert_equal (hex_string .status , 200 )
82250 assert_greater_than (int (response .getheader ('content-length' )), 10 )
251+
252+
83253
84254 # check block tx details
85255 # let's make 3 tx and mine them on node 1
0 commit comments