4848 hexlify = lambda b : binascii .hexlify (b ).decode ('utf8' )
4949
5050
51- class JSONRPCException (Exception ):
51+ class JSONRPCError (Exception ):
52+ """JSON-RPC protocol error"""
53+
5254 def __init__ (self , rpc_error ):
53- super (JSONRPCException , self ).__init__ ('msg: %r code: %r' %
54- (rpc_error ['message' ], rpc_error ['code' ]))
55+ super (JSONRPCException , self ).__init__ (
56+ 'msg: %r code: %r' %
57+ (rpc_error ['message' ], rpc_error ['code' ]))
5558 self .error = rpc_error
5659
5760
58- class RawProxy (object ):
59- def __init__ (self , service_url = None ,
60- service_port = None ,
61- btc_conf_file = None ,
62- timeout = DEFAULT_HTTP_TIMEOUT ):
63- """Low-level JSON-RPC proxy
61+ # 0.4.0 compatibility
62+ JSONRPCException = JSONRPCError
6463
65- Unlike Proxy no conversion is done from the raw JSON objects.
66- """
64+
65+ class BaseProxy (object ):
66+ """Base JSON-RPC proxy class. Contains only private methods; do not use
67+ directly."""
68+
69+ def __init__ (self ,
70+ service_url = None ,
71+ service_port = None ,
72+ btc_conf_file = None ,
73+ timeout = DEFAULT_HTTP_TIMEOUT ):
6774
6875 if service_url is None :
6976 # Figure out the path to the bitcoin.conf file
@@ -167,28 +174,14 @@ def _call(self, service_name, *args):
167174
168175 response = self ._get_response ()
169176 if response ['error' ] is not None :
170- raise JSONRPCException (response ['error' ])
177+ raise JSONRPCError (response ['error' ])
171178 elif 'result' not in response :
172- raise JSONRPCException ({
179+ raise JSONRPCError ({
173180 'code' : - 343 , 'message' : 'missing JSON-RPC result' })
174181 else :
175182 return response ['result' ]
176183
177184
178- def __getattr__ (self , name ):
179- if name .startswith ('__' ) and name .endswith ('__' ):
180- # Python internal stuff
181- raise AttributeError
182-
183- # Create a callable to do the actual call
184- f = lambda * args : self ._call (name , * args )
185-
186- # Make debuggers show <function bitcoin.rpc.name> rather than <function
187- # bitcoin.rpc.<lambda>>
188- f .__name__ = name
189- return f
190-
191-
192185 def _batch (self , rpc_call_list ):
193186 postdata = json .dumps (list (rpc_call_list ))
194187 self .__conn .request ('POST' , self .__url .path , postdata ,
@@ -202,7 +195,7 @@ def _batch(self, rpc_call_list):
202195 def _get_response (self ):
203196 http_response = self .__conn .getresponse ()
204197 if http_response is None :
205- raise JSONRPCException ({
198+ raise JSONRPCError ({
206199 'code' : - 342 , 'message' : 'missing HTTP response from server' })
207200
208201 return json .loads (http_response .read ().decode ('utf8' ),
@@ -212,33 +205,79 @@ def __del__(self):
212205 self .__conn .close ()
213206
214207
215- class Proxy (RawProxy ):
216- def __init__ (self , service_url = None ,
217- service_port = None ,
218- btc_conf_file = None ,
219- timeout = DEFAULT_HTTP_TIMEOUT ,
220- ** kwargs ):
221- """Create a proxy to a bitcoin RPC service
208+ class RawProxy (BaseProxy ):
209+ """Low-level proxy to a bitcoin JSON-RPC service
210+
211+ Unlike ``Proxy``, no conversion is done besides parsing JSON. As far as
212+ Python is concerned, you can call any method; ``JSONRPCError`` will be
213+ raised if the server does not recognize it.
214+ """
215+ def __init__ (self ,
216+ service_url = None ,
217+ service_port = None ,
218+ btc_conf_file = None ,
219+ timeout = DEFAULT_HTTP_TIMEOUT ,
220+ ** kwargs ):
221+ super (RawProxy , self ).__init__ (service_url = service_url ,
222+ service_port = service_port ,
223+ btc_conf_file = btc_conf_file ,
224+ timeout = timeout ,
225+ ** kwargs )
222226
223- Unlike RawProxy data is passed as objects, rather than JSON. (not yet
224- fully implemented) Assumes Bitcoin Core version >= 0.9; older versions
225- mostly work, but there are a few incompatibilities.
227+ def __getattr__ (self , name ):
228+ if name .startswith ('__' ) and name .endswith ('__' ):
229+ # Python internal stuff
230+ raise AttributeError
231+
232+ # Create a callable to do the actual call
233+ f = lambda * args : self ._call (name , * args )
234+
235+ # Make debuggers show <function bitcoin.rpc.name> rather than <function
236+ # bitcoin.rpc.<lambda>>
237+ f .__name__ = name
238+ return f
226239
227- If service_url is not specified the username and password are read out
228- of the file btc_conf_file. If btc_conf_file is not specified
229- ~/.bitcoin/bitcoin.conf or equivalent is used by default. The default
230- port is set according to the chain parameters in use: mainnet, testnet,
231- or regtest.
232240
233- Usually no arguments to Proxy() are needed; the local bitcoind will be
234- used.
241+ class Proxy (BaseProxy ):
242+ """Proxy to a bitcoin RPC service
235243
236- timeout - timeout in seconds before the HTTP interface times out
244+ Unlike ``RawProxy``, data is passed as ``bitcoin.core`` objects or packed
245+ bytes, rather than JSON or hex strings. Not all methods are implemented
246+ yet; you can use ``call`` to access missing ones in a forward-compatible
247+ way. Assumes Bitcoin Core version >= 0.9; older versions mostly work, but
248+ there are a few incompatibilities.
249+ """
250+
251+ def __init__ (self ,
252+ service_url = None ,
253+ service_port = None ,
254+ btc_conf_file = None ,
255+ timeout = DEFAULT_HTTP_TIMEOUT ,
256+ ** kwargs ):
257+ """Create a proxy object
258+
259+ If ``service_url`` is not specified, the username and password are read
260+ out of the file ``btc_conf_file``. If ``btc_conf_file`` is not
261+ specified, ``~/.bitcoin/bitcoin.conf`` or equivalent is used by
262+ default. The default port is set according to the chain parameters in
263+ use: mainnet, testnet, or regtest.
264+
265+ Usually no arguments to ``Proxy()`` are needed; the local bitcoind will
266+ be used.
267+
268+ ``timeout`` - timeout in seconds before the HTTP interface times out
237269 """
238- super (Proxy , self ).__init__ (service_url = service_url , service_port = service_port , btc_conf_file = btc_conf_file ,
270+
271+ super (Proxy , self ).__init__ (service_url = service_url ,
272+ service_port = service_port ,
273+ btc_conf_file = btc_conf_file ,
239274 timeout = timeout ,
240275 ** kwargs )
241276
277+ def call (self , service_name , * args ):
278+ """Call an RPC method by name and raw (JSON encodable) arguments"""
279+ return self ._call (service_name , * args )
280+
242281 def dumpprivkey (self , addr ):
243282 """Return the private key matching an address
244283 """
@@ -247,19 +286,26 @@ def dumpprivkey(self, addr):
247286 return CBitcoinSecret (r )
248287
249288 def getaccountaddress (self , account = None ):
250- """Return the current Bitcoin address for receiving payments to this account."""
289+ """Return the current Bitcoin address for receiving payments to this
290+ account."""
251291 r = self ._call ('getaccountaddress' , account )
252292 return CBitcoinAddress (r )
253293
254294 def getbalance (self , account = '*' , minconf = 1 ):
255295 """Get the balance
256296
257- account - The selected account. Defaults to "*" for entire wallet. It may be the default account using "".
258- minconf - Only include transactions confirmed at least this many times. (default=1)
297+ account - The selected account. Defaults to "*" for entire wallet. It
298+ may be the default account using "".
299+ minconf - Only include transactions confirmed at least this many times.
300+ (default=1)
259301 """
260302 r = self ._call ('getbalance' , account , minconf )
261303 return int (r * COIN )
262304
305+ def getbestblockhash (self ):
306+ """Return hash of best (tip) block in longest block chain."""
307+ return lx (self ._call ('getbestblockhash' ))
308+
263309 def getblock (self , block_hash ):
264310 """Get block <block_hash>
265311
@@ -272,30 +318,38 @@ def getblock(self, block_hash):
272318 (self .__class__ .__name__ , block_hash .__class__ ))
273319 try :
274320 r = self ._call ('getblock' , block_hash , False )
275- except JSONRPCException as ex :
321+ except JSONRPCError as ex :
276322 raise IndexError ('%s.getblock(): %s (%d)' %
277323 (self .__class__ .__name__ , ex .error ['message' ], ex .error ['code' ]))
278324 return CBlock .deserialize (unhexlify (r ))
279325
326+ def getblockcount (self ):
327+ """Return the number of blocks in the longest block chain"""
328+ return self ._call ('getblockcount' )
329+
280330 def getblockhash (self , height ):
281331 """Return hash of block in best-block-chain at height.
282332
283333 Raises IndexError if height is not valid.
284334 """
285335 try :
286336 return lx (self ._call ('getblockhash' , height ))
287- except JSONRPCException as ex :
337+ except JSONRPCError as ex :
288338 raise IndexError ('%s.getblockhash(): %s (%d)' %
289339 (self .__class__ .__name__ , ex .error ['message' ], ex .error ['code' ]))
290340
291341 def getinfo (self ):
292- """Return an object containing various state info"""
342+ """Return a JSON object containing various state info"""
293343 r = self ._call ('getinfo' )
294344 if 'balance' in r :
295345 r ['balance' ] = int (r ['balance' ] * COIN )
296346 r ['paytxfee' ] = int (r ['paytxfee' ] * COIN )
297347 return r
298348
349+ def getmininginfo (self ):
350+ """Return a JSON object containing mining-related information"""
351+ return self ._call ('getmininginfo' )
352+
299353 def getnewaddress (self , account = None ):
300354 """Return a new Bitcoin address for receiving payments.
301355
@@ -341,7 +395,7 @@ def getrawtransaction(self, txid, verbose=False):
341395 """
342396 try :
343397 r = self ._call ('getrawtransaction' , b2lx (txid ), 1 if verbose else 0 )
344- except JSONRPCException as ex :
398+ except JSONRPCError as ex :
345399 raise IndexError ('%s.getrawtransaction(): %s (%d)' %
346400 (self .__class__ .__name__ , ex .error ['message' ], ex .error ['code' ]))
347401 if verbose :
@@ -368,7 +422,8 @@ def getreceivedbyaddress(self, addr, minconf=1):
368422 always show zero.
369423
370424 addr - The address. (CBitcoinAddress instance)
371- minconf - Only include transactions confirmed at least this many times. (default=1)
425+ minconf - Only include transactions confirmed at least this many times.
426+ (default=1)
372427 """
373428 r = self ._call ('getreceivedbyaddress' , str (addr ), minconf )
374429 return int (r * COIN )
@@ -382,7 +437,7 @@ def gettransaction(self, txid):
382437 """
383438 try :
384439 r = self ._call ('gettransaction' , b2lx (txid ))
385- except JSONRPCException as ex :
440+ except JSONRPCError as ex :
386441 raise IndexError ('%s.getrawtransaction(): %s (%d)' %
387442 (self .__class__ .__name__ , ex .error ['message' ], ex .error ['code' ]))
388443 return r
@@ -441,7 +496,8 @@ def listunspent(self, minconf=0, maxconf=9999999, addrs=None):
441496
442497 def lockunspent (self , unlock , outpoints ):
443498 """Lock or unlock outpoints"""
444- json_outpoints = [{'txid' :b2lx (outpoint .hash ),'vout' :outpoint .n } for outpoint in outpoints ]
499+ json_outpoints = [{'txid' :b2lx (outpoint .hash ), 'vout' :outpoint .n }
500+ for outpoint in outpoints ]
445501 return self ._call ('lockunspent' , unlock , json_outpoints )
446502
447503 def sendrawtransaction (self , tx , allowhighfees = False ):
@@ -459,7 +515,8 @@ def sendrawtransaction(self, tx, allowhighfees=False):
459515
460516 def sendmany (self , fromaccount , payments , minconf = 1 , comment = '' ):
461517 """Sent amount to a given address"""
462- json_payments = {str (addr ):float (amount )/ COIN for addr ,amount in payments .items ()}
518+ json_payments = {str (addr ):float (amount )/ COIN
519+ for addr , amount in payments .items ()}
463520 r = self ._call ('sendmany' , fromaccount , json_payments , minconf , comment )
464521 return lx (r )
465522
@@ -516,7 +573,8 @@ def removenode(self, node):
516573 return self ._addnode (node , 'remove' )
517574
518575__all__ = (
519- 'JSONRPCException' ,
520- 'RawProxy' ,
521- 'Proxy' ,
576+ 'JSONRPCError' ,
577+ 'JSONRPCException' ,
578+ 'RawProxy' ,
579+ 'Proxy' ,
522580)
0 commit comments