URI:
       tjsonrpc.py - electrum-personal-server - Maximally lightweight electrum server for a single user
  HTML git clone https://git.parazyd.org/electrum-personal-server
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
       tjsonrpc.py (5094B)
       ---
            1 # Copyright (C) 2013,2015 by Daniel Kraft <d@domob.eu>
            2 # Copyright (C) 2014 by phelix / blockchained.com
            3 
            4 #jsonrpc.py from https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/jmclient/jmclient/jsonrpc.py
            5 
            6 import base64
            7 import http.client
            8 import json
            9 
           10 class JsonRpcError(Exception): pass
           11 class JsonRpcConnectionError(JsonRpcError): pass
           12 
           13 class JsonRpc(object):
           14     """
           15     Simple implementation of a JSON-RPC client that is used
           16     to connect to Bitcoin.
           17     """
           18     def __init__(self, host, port, user, password, cookie_path=None,
           19             wallet_filename="", logger=None):
           20         self.host = host
           21         self.port = port
           22 
           23         self.cookie_path = cookie_path
           24         if cookie_path:
           25             self.load_from_cookie()
           26         else:
           27             self.create_authstr(user, password)
           28 
           29         self.conn = http.client.HTTPConnection(self.host, self.port)
           30         if len(wallet_filename) > 0:
           31             self.url = "/wallet/" + wallet_filename
           32         else:
           33             self.url = ""
           34         self.logger = logger
           35         self.queryId = 1
           36 
           37     def create_authstr(self, username, password):
           38         self.authstr = "%s:%s" % (username, password)
           39 
           40     def load_from_cookie(self):
           41         fd = open(self.cookie_path)
           42         username, password = fd.read().strip().split(":")
           43         fd.close()
           44         self.create_authstr(username, password)
           45 
           46     def queryHTTP(self, obj):
           47         """
           48         Send an appropriate HTTP query to the server.  The JSON-RPC
           49         request should be (as object) in 'obj'.  If the call succeeds,
           50         the resulting JSON object is returned.  In case of an error
           51         with the connection (not JSON-RPC itself), an exception is raised.
           52         """
           53         headers = {"User-Agent": "electrum-personal-server",
           54                    "Content-Type": "application/json",
           55                    "Accept": "application/json"}
           56         headers["Authorization"] = (b"Basic " +
           57             base64.b64encode(self.authstr.encode('utf-8')))
           58         body = json.dumps(obj)
           59         auth_failed_once = False
           60         for i in range(20):
           61             try:
           62                 self.conn.request("POST", self.url, body, headers)
           63                 response = self.conn.getresponse()
           64                 if response.status == 401:
           65                     if self.cookie_path == None or auth_failed_once:
           66                         self.conn.close()
           67                         raise JsonRpcConnectionError(
           68                                 "authentication for JSON-RPC failed")
           69                     else:
           70                         auth_failed_once = True
           71                         #try reloading u/p from the cookie file once
           72                         self.load_from_cookie()
           73                         raise OSError() #jump to error handler below
           74                 auth_failed_once = False
           75                 #All the codes below are 'fine' from a JSON-RPC point of view.
           76                 if response.status not in [200, 404, 500]:
           77                     self.conn.close()
           78                     raise JsonRpcConnectionError("unknown error in JSON-RPC")
           79                 data = response.read()
           80                 return json.loads(data.decode('utf-8'))
           81             except JsonRpcConnectionError as exc:
           82                 raise exc
           83             except http.client.BadStatusLine:
           84                 return "CONNFAILURE"
           85             except OSError:
           86                     # connection dropped, reconnect
           87                     try:
           88                         self.conn.close()
           89                         self.conn.connect()
           90                     except ConnectionError as e:
           91                         #node probably offline, notify with jsonrpc error
           92                         raise JsonRpcConnectionError(repr(e))
           93                     continue
           94             except Exception as exc:
           95                 raise JsonRpcConnectionError("JSON-RPC connection failed. Err:"
           96                     + repr(exc))
           97             break
           98         return None
           99 
          100     def call(self, method, params):
          101         currentId = self.queryId
          102         self.queryId += 1
          103 
          104         request = {"method": method, "params": params, "id": currentId}
          105         #query can fail from keepalive timeout; keep retrying if it does, up
          106         #to a reasonable limit, then raise (failure to access blockchain
          107         #is a critical failure). Note that a real failure to connect (e.g.
          108         #wrong port) is raised in queryHTTP directly.
          109         response_received = False
          110         for i in range(100):
          111             response = self.queryHTTP(request)
          112             if response != "CONNFAILURE":
          113                 response_received = True
          114                 break
          115             #Failure means keepalive timed out, just make a new one
          116             self.conn = http.client.HTTPConnection(self.host, self.port)
          117             self.logger.debug("Creating new jsonrpc HTTPConnection")
          118         if not response_received:
          119             raise JsonRpcConnectionError("Unable to connect over RPC")
          120         if response["id"] != currentId:
          121             raise JsonRpcConnectionError("invalid id returned by query")
          122         if response["error"] is not None:
          123             raise JsonRpcError(response["error"])
          124         return response["result"]