#!/usr/bin/python ''' Socket-based, Bitcoin-compatible JSON-RPC v1.0 client. By: Eric Swanson (http://www.alloscomp.com/bitcoin) Version 0.1, July 21, 2010 Don't use this for one-off request->response pairs. Use something like the reference python-jsonrpc library, or urllib2 + json. This client is hackish, but it works for me (and has sped up my JSON-RPC accesses tremendously). For details of WHY exactly I felt the need to redo python-jsonrpc using a raw socket, check out the follow forum post: http://www.bitcoin.org/smf/index.php?topic=528.0 Usage is fairly straightforward, and a code sample can be found below the library code (in the if __name__=='__main__' clause). ''' import json,socket class JSONRPCError(Exception): pass class _Call: def __init__(self,method,service): self.method = method self.service = service def __call__(self,*args,**kwargs): # Sort out a reasonable ID id = 'jss-{0}'.format(self.service.nextID) if len(kwargs) > 0: if len(kwargs) > 1 or 'id' not in kwargs: raise JSONRPCError("Bitcoin's JSON-RPC 1.0 implementation doesn't support keyword args.") else: id = kwargs['id'] if id in self.service.callQueue: raise JSONRPCError("Error: The call ID '{0}' is already used.".format(id)) if id[:4] == 'jss-': self.service.nextID += 1 # Append to the call queue self.service.callQueue[id] = json.dumps({"method":self.method,"params":args,"id":id}) return id class Service: def __init__(self,host='localhost',port=8332,path='/',simple=True): self.host = host self.port = port self.path = path self.nextID = 1 self.callQueue = {} self.simple = simple def __getSocket(self): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((self.host, self.port)) return client_socket def __getattr__(self,name): return _Call(method=name,service=self) def __call__(self): self.responses = self.__makeCalls(self.callQueue) self.callQueue = [] if self.simple: simpler = {} for r in self.responses: if r['error']: raise JSONRPCError(r['error']) else: simpler[r['id']] = r['result'] return simpler else: return self.responses def __makeCalls(self,calls): if isinstance(calls,dict): calls = list(calls.values()) # Build request cstr = '\n'.join([str(c) for c in calls]) request = '''POST {0.path} HTTP/1.1 Accept: application/json-rpc Content-Type: application/json-rpc Content-Length: {length} {calls} '''.format(self,calls=cstr,length=len(cstr)) # Make request s = self.__getSocket() s.send(request) response = '' while s: data = s.recv(512) if not data: break response += data s.close() #print '-'*80 #print response #print '-'*80 # Parse request responses = [] for line in response.split('\n'): if line.find('":') != -1 and line.find('{') != -1: # HACK HACK HACK! responses.append(json.loads(line)) return responses if __name__ == '__main__': print __doc__ print #from btcjsonrpc import Service # Obviously don't need this here s = Service() print 'preparing getbalance; id:',s.getbalance() # Each call returns its ID so you can find it later in the results print 'preparing getdifficulty; id:',s.getdifficulty() print 'preparing listreceivedbyaddress; id:',s.listreceivedbyaddress(10000) # Call with a parameter print 'preparing getbalance; id:',s.getbalance(id='getbalance 2') # You can also specify your own ID print '\nexecuting call\n\nresults:' results = s() # Get the results by calling the Service object for id,value in results.iteritems(): print id,value # If you'd prefer to work directly with the JSON responses instead of a dict of IDs, then access the list Service.responses. print '\njson responses' print s.responses