14
14
import subprocess
15
15
16
16
17
- RPC_INVALID_PARAMETER = - 8
18
- RPC_METHOD_NOT_FOUND = - 32601
19
- RPC_INVALID_REQUEST = - 32600
17
+ RPC_INVALID_ADDRESS_OR_KEY = - 5
18
+ RPC_INVALID_PARAMETER = - 8
19
+ RPC_METHOD_NOT_FOUND = - 32601
20
+ RPC_INVALID_REQUEST = - 32600
21
+ RPC_PARSE_ERROR = - 32700
20
22
21
23
22
24
@dataclass
@@ -98,9 +100,7 @@ def test_getrpcinfo(self):
98
100
assert_greater_than_or_equal (command ['duration' ], 0 )
99
101
assert_equal (info ['logpath' ], os .path .join (self .nodes [0 ].chain_path , 'debug.log' ))
100
102
101
- def test_batch_request (self ):
102
- self .log .info ("Testing basic JSON-RPC batch request..." )
103
-
103
+ def test_batch_request (self , call_options ):
104
104
calls = [
105
105
# A basic request that will work fine.
106
106
{"method" : "getblockcount" },
@@ -109,6 +109,8 @@ def test_batch_request(self):
109
109
{"method" : "invalidmethod" },
110
110
# Another call that should succeed.
111
111
{"method" : "getblockhash" , "params" : [0 ]},
112
+ # Invalid request format
113
+ {"pizza" : "sausage" }
112
114
]
113
115
results = [
114
116
{"result" : 0 },
@@ -120,19 +122,94 @@ def test_batch_request(self):
120
122
request = []
121
123
response = []
122
124
for idx , (call , result ) in enumerate (zip (calls , results ), 1 ):
123
- options = BatchOptions ()
125
+ options = call_options (idx )
126
+ if options is None :
127
+ continue
124
128
request .append (format_request (options , idx , call ))
125
129
response .append (format_response (options , idx , result ))
126
130
127
131
rpc_response , http_status = send_json_rpc (self .nodes [0 ], request )
128
132
assert_equal (http_status , 200 )
129
133
assert_equal (rpc_response , response )
130
134
131
- def test_http_status_codes (self ):
132
- self .log .info ("Testing HTTP status codes for JSON-RPC requests..." )
135
+ def test_batch_requests (self ):
136
+ self .log .info ("Testing empty batch request..." )
137
+ self .test_batch_request (lambda idx : None )
138
+
139
+ self .log .info ("Testing basic JSON-RPC 2.0 batch request..." )
140
+ self .test_batch_request (lambda idx : BatchOptions (version = 2 ))
141
+
142
+ self .log .info ("Testing JSON-RPC 2.0 batch with notifications..." )
143
+ self .test_batch_request (lambda idx : BatchOptions (version = 2 , notification = idx < 2 ))
144
+
145
+ self .log .info ("Testing JSON-RPC 2.0 batch of ALL notifications..." )
146
+ self .test_batch_request (lambda idx : BatchOptions (version = 2 , notification = True ))
147
+
148
+ # JSONRPC 1.1 does not support batch requests, but test them for backwards compatibility.
149
+ self .log .info ("Testing nonstandard JSON-RPC 1.1 batch request..." )
150
+ self .test_batch_request (lambda idx : BatchOptions (version = 1 ))
133
151
152
+ self .log .info ("Testing nonstandard mixed JSON-RPC 1.1/2.0 batch request..." )
153
+ self .test_batch_request (lambda idx : BatchOptions (version = 2 if idx % 2 else 1 ))
154
+
155
+ self .log .info ("Testing nonstandard batch request without version numbers..." )
156
+ self .test_batch_request (lambda idx : BatchOptions ())
157
+
158
+ self .log .info ("Testing nonstandard batch request without version numbers or ids..." )
159
+ self .test_batch_request (lambda idx : BatchOptions (notification = True ))
160
+
161
+ self .log .info ("Testing nonstandard jsonrpc 1.0 version number is accepted..." )
162
+ self .test_batch_request (lambda idx : BatchOptions (request_fields = {"jsonrpc" : "1.0" }))
163
+
164
+ self .log .info ("Testing unrecognized jsonrpc version number is accepted..." )
165
+ self .test_batch_request (lambda idx : BatchOptions (request_fields = {"jsonrpc" : "2.1" }))
166
+
167
+ def test_http_status_codes (self ):
168
+ self .log .info ("Testing HTTP status codes for JSON-RPC 1.1 requests..." )
169
+ # OK
170
+ expect_http_rpc_status (200 , None , self .nodes [0 ], "getblockhash" , [0 ])
171
+ # Errors
134
172
expect_http_rpc_status (404 , RPC_METHOD_NOT_FOUND , self .nodes [0 ], "invalidmethod" , [])
135
173
expect_http_rpc_status (500 , RPC_INVALID_PARAMETER , self .nodes [0 ], "getblockhash" , [42 ])
174
+ # force-send empty request
175
+ response , status = send_raw_rpc (self .nodes [0 ], b"" )
176
+ assert_equal (response , {"id" : None , "result" : None , "error" : {"code" : RPC_PARSE_ERROR , "message" : "Parse error" }})
177
+ assert_equal (status , 500 )
178
+ # force-send invalidly formatted request
179
+ response , status = send_raw_rpc (self .nodes [0 ], b"this is bad" )
180
+ assert_equal (response , {"id" : None , "result" : None , "error" : {"code" : RPC_PARSE_ERROR , "message" : "Parse error" }})
181
+ assert_equal (status , 500 )
182
+
183
+ self .log .info ("Testing HTTP status codes for JSON-RPC 2.0 requests..." )
184
+ # OK
185
+ expect_http_rpc_status (200 , None , self .nodes [0 ], "getblockhash" , [0 ], 2 , False )
186
+ # RPC errors and HTTP errors
187
+ expect_http_rpc_status (404 , RPC_METHOD_NOT_FOUND , self .nodes [0 ], "invalidmethod" , [], 2 , False )
188
+ expect_http_rpc_status (500 , RPC_INVALID_PARAMETER , self .nodes [0 ], "getblockhash" , [42 ], 2 , False )
189
+ # force-send invalidly formatted requests
190
+ response , status = send_json_rpc (self .nodes [0 ], {"jsonrpc" : 2 , "method" : "getblockcount" })
191
+ assert_equal (response , {"error" : None , "id" : None , "result" : 0 })
192
+ assert_equal (status , 200 )
193
+ response , status = send_json_rpc (self .nodes [0 ], {"jsonrpc" : "3.0" , "method" : "getblockcount" })
194
+ assert_equal (response , {"error" : None , "id" : None , "result" : 0 })
195
+ assert_equal (status , 200 )
196
+
197
+ self .log .info ("Testing HTTP status codes for JSON-RPC 2.0 notifications..." )
198
+ # Not notification: id exists
199
+ response , status = send_json_rpc (self .nodes [0 ], {"jsonrpc" : "2.0" , "id" : None , "method" : "getblockcount" })
200
+ assert_equal (response ["result" ], 0 )
201
+ assert_equal (status , 200 )
202
+ # Not notification: JSON 1.1
203
+ expect_http_rpc_status (200 , None , self .nodes [0 ], "getblockcount" , [], 1 )
204
+ # Not notification: has "id" field
205
+ expect_http_rpc_status (200 , None , self .nodes [0 ], "getblockcount" , [], 2 , False )
206
+ block_count = self .nodes [0 ].getblockcount ()
207
+ expect_http_rpc_status (200 , None , self .nodes [0 ], "generatetoaddress" , [1 , "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqdku202" ], 2 , True )
208
+ # The command worked even though there was no response
209
+ assert_equal (block_count + 1 , self .nodes [0 ].getblockcount ())
210
+ expect_http_rpc_status (500 , RPC_INVALID_ADDRESS_OR_KEY , self .nodes [0 ], "generatetoaddress" , [1 , "invalid_address" ], 2 , True )
211
+ # Sanity check: command was not executed
212
+ assert_equal (block_count + 1 , self .nodes [0 ].getblockcount ())
136
213
137
214
def test_work_queue_exceeded (self ):
138
215
self .log .info ("Testing work queue exceeded..." )
@@ -148,7 +225,7 @@ def test_work_queue_exceeded(self):
148
225
149
226
def run_test (self ):
150
227
self .test_getrpcinfo ()
151
- self .test_batch_request ()
228
+ self .test_batch_requests ()
152
229
self .test_http_status_codes ()
153
230
self .test_work_queue_exceeded ()
154
231
0 commit comments