10
10
import json
11
11
import logging
12
12
import os
13
+ import re
13
14
import subprocess
14
15
import time
15
16
22
23
p2p_port ,
23
24
)
24
25
26
+ # For Python 3.4 compatibility
27
+ JSONDecodeError = getattr (json , "JSONDecodeError" , ValueError )
28
+
25
29
BITCOIND_PROC_WAIT_TIMEOUT = 60
26
30
27
31
class TestNode ():
@@ -38,7 +42,7 @@ class TestNode():
38
42
To make things easier for the test writer, any unrecognised messages will
39
43
be dispatched to the RPC connection."""
40
44
41
- def __init__ (self , i , dirname , extra_args , rpchost , timewait , binary , stderr , mocktime , coverage_dir ):
45
+ def __init__ (self , i , dirname , extra_args , rpchost , timewait , binary , stderr , mocktime , coverage_dir , use_cli = False ):
42
46
self .index = i
43
47
self .datadir = os .path .join (dirname , "node" + str (i ))
44
48
self .rpchost = rpchost
@@ -58,6 +62,7 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo
58
62
self .args = [self .binary , "-datadir=" + self .datadir , "-server" , "-keypool=1" , "-discover=0" , "-rest" , "-logtimemicros" , "-debug" , "-debugexclude=libevent" , "-debugexclude=leveldb" , "-mocktime=" + str (mocktime ), "-uacomment=testnode%d" % i ]
59
63
60
64
self .cli = TestNodeCLI (os .getenv ("BITCOINCLI" , "bitcoin-cli" ), self .datadir )
65
+ self .use_cli = use_cli
61
66
62
67
self .running = False
63
68
self .process = None
@@ -69,9 +74,12 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo
69
74
self .p2ps = []
70
75
71
76
def __getattr__ (self , name ):
72
- """Dispatches any unrecognised messages to the RPC connection."""
73
- assert self .rpc_connected and self .rpc is not None , "Error: no RPC connection"
74
- return getattr (self .rpc , name )
77
+ """Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
78
+ if self .use_cli :
79
+ return getattr (self .cli , name )
80
+ else :
81
+ assert self .rpc_connected and self .rpc is not None , "Error: no RPC connection"
82
+ return getattr (self .rpc , name )
75
83
76
84
def start (self , extra_args = None , stderr = None ):
77
85
"""Start the node."""
@@ -110,10 +118,13 @@ def wait_for_rpc_connection(self):
110
118
raise AssertionError ("Unable to connect to bitcoind" )
111
119
112
120
def get_wallet_rpc (self , wallet_name ):
113
- assert self .rpc_connected
114
- assert self .rpc
115
- wallet_path = "wallet/%s" % wallet_name
116
- return self .rpc / wallet_path
121
+ if self .use_cli :
122
+ return self .cli ("-rpcwallet={}" .format (wallet_name ))
123
+ else :
124
+ assert self .rpc_connected
125
+ assert self .rpc
126
+ wallet_path = "wallet/%s" % wallet_name
127
+ return self .rpc / wallet_path
117
128
118
129
def stop_node (self ):
119
130
"""Stop the node."""
@@ -187,6 +198,16 @@ def disconnect_p2ps(self):
187
198
p .peer_disconnect ()
188
199
del self .p2ps [:]
189
200
201
+ class TestNodeCLIAttr :
202
+ def __init__ (self , cli , command ):
203
+ self .cli = cli
204
+ self .command = command
205
+
206
+ def __call__ (self , * args , ** kwargs ):
207
+ return self .cli .send_cli (self .command , * args , ** kwargs )
208
+
209
+ def get_request (self , * args , ** kwargs ):
210
+ return lambda : self (* args , ** kwargs )
190
211
191
212
class TestNodeCLI ():
192
213
"""Interface to bitcoin-cli for an individual node"""
@@ -196,17 +217,26 @@ def __init__(self, binary, datadir):
196
217
self .binary = binary
197
218
self .datadir = datadir
198
219
self .input = None
220
+ self .log = logging .getLogger ('TestFramework.bitcoincli' )
199
221
200
222
def __call__ (self , * args , input = None ):
201
223
# TestNodeCLI is callable with bitcoin-cli command-line args
202
- self .args = [str (arg ) for arg in args ]
203
- self .input = input
204
- return self
224
+ cli = TestNodeCLI (self .binary , self .datadir )
225
+ cli .args = [str (arg ) for arg in args ]
226
+ cli .input = input
227
+ return cli
205
228
206
229
def __getattr__ (self , command ):
207
- def dispatcher (* args , ** kwargs ):
208
- return self .send_cli (command , * args , ** kwargs )
209
- return dispatcher
230
+ return TestNodeCLIAttr (self , command )
231
+
232
+ def batch (self , requests ):
233
+ results = []
234
+ for request in requests :
235
+ try :
236
+ results .append (dict (result = request ()))
237
+ except JSONRPCException as e :
238
+ results .append (dict (error = e ))
239
+ return results
210
240
211
241
def send_cli (self , command , * args , ** kwargs ):
212
242
"""Run bitcoin-cli command. Deserializes returned string as python object."""
@@ -218,10 +248,18 @@ def send_cli(self, command, *args, **kwargs):
218
248
if named_args :
219
249
p_args += ["-named" ]
220
250
p_args += [command ] + pos_args + named_args
251
+ self .log .debug ("Running bitcoin-cli command: %s" % command )
221
252
process = subprocess .Popen (p_args , stdin = subprocess .PIPE , stdout = subprocess .PIPE , stderr = subprocess .PIPE , universal_newlines = True )
222
253
cli_stdout , cli_stderr = process .communicate (input = self .input )
223
254
returncode = process .poll ()
224
255
if returncode :
256
+ match = re .match (r'error code: ([-0-9]+)\nerror message:\n(.*)' , cli_stderr )
257
+ if match :
258
+ code , message = match .groups ()
259
+ raise JSONRPCException (dict (code = int (code ), message = message ))
225
260
# Ignore cli_stdout, raise with cli_stderr
226
261
raise subprocess .CalledProcessError (returncode , self .binary , output = cli_stderr )
227
- return json .loads (cli_stdout , parse_float = decimal .Decimal )
262
+ try :
263
+ return json .loads (cli_stdout , parse_float = decimal .Decimal )
264
+ except JSONDecodeError :
265
+ return cli_stdout .rstrip ("\n " )
0 commit comments