diff --git a/bitcoin/core/__init__.py b/bitcoin/core/__init__.py index ea578f9b..3f2d548a 100644 --- a/bitcoin/core/__init__.py +++ b/bitcoin/core/__init__.py @@ -727,7 +727,7 @@ class CoreMainParams(CoreChainParams): PROOF_OF_WORK_LIMIT = 2**256-1 >> 32 class CoreTestNetParams(CoreMainParams): - NAME = 'testnet' + NAME = 'testnet3' GENESIS_BLOCK = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) class CoreSigNetParams(CoreMainParams): diff --git a/bitcoin/rpc.py b/bitcoin/rpc.py index 3155b726..56f2f9e8 100644 --- a/bitcoin/rpc.py +++ b/bitcoin/rpc.py @@ -128,6 +128,44 @@ class InWarmupError(JSONRPCError): RPC_ERROR_CODE = -28 +def default_btc_dir(): + if platform.system() == 'Darwin': + return os.path.expanduser('~/Library/Application Support/Bitcoin/') + elif platform.system() == 'Windows': + return os.path.join(os.environ['APPDATA'], 'Bitcoin') + return os.path.expanduser('~/.bitcoin') + + +def parse_conf_file(file_object): + conf = {} + for line in file_object.readlines(): + if '#' in line: + line = line[:line.index('#')] + if '=' not in line: + continue + k, v = line.split('=', 1) + conf[k.strip()] = v.strip() + return conf + + +def get_authpair(conf, network, btc_conf_file): + cookie_dir = conf.get('datadir', os.path.dirname(btc_conf_file)) + if network != "mainnet": + cookie_dir = os.path.join(cookie_dir, network) + cookie_file = os.path.join(cookie_dir, ".cookie") + + try: + with open(cookie_file, 'r') as fd: + return fd.read() + except IOError as err: + if 'rpcpassword' in conf: + return "%s:%s" % (conf['rpcuser'], conf['rpcpassword']) + + raise ValueError('Cookie file unusable (%s) and rpcpassword ' + 'not specified in the configuration file: %r' + % (err, btc_conf_file)) + + class BaseProxy(object): """Base JSON-RPC proxy class. Contains only private methods; do not use directly.""" @@ -148,13 +186,7 @@ def __init__(self, if service_url is None: # Figure out the path to the bitcoin.conf file if btc_conf_file is None: - if platform.system() == 'Darwin': - btc_conf_file = os.path.expanduser('~/Library/Application Support/Bitcoin/') - elif platform.system() == 'Windows': - btc_conf_file = os.path.join(os.environ['APPDATA'], 'Bitcoin') - else: - btc_conf_file = os.path.expanduser('~/.bitcoin') - btc_conf_file = os.path.join(btc_conf_file, 'bitcoin.conf') + btc_conf_file = os.path.join(default_btc_dir(), 'bitcoin.conf') # Bitcoin Core accepts empty rpcuser, not specified in btc_conf_file conf = {'rpcuser': ""} @@ -162,14 +194,7 @@ def __init__(self, # Extract contents of bitcoin.conf to build service_url try: with open(btc_conf_file, 'r') as fd: - for line in fd.readlines(): - if '#' in line: - line = line[:line.index('#')] - if '=' not in line: - continue - k, v = line.split('=', 1) - conf[k.strip()] = v.strip() - + conf.update(parse_conf_file(fd)) # Treat a missing bitcoin.conf as though it were empty except FileNotFoundError: pass @@ -182,19 +207,7 @@ def __init__(self, service_url = ('%s://%s:%d' % ('http', conf['rpchost'], conf['rpcport'])) - cookie_dir = conf.get('datadir', os.path.dirname(btc_conf_file)) - if bitcoin.params.NAME != "mainnet": - cookie_dir = os.path.join(cookie_dir, bitcoin.params.NAME) - cookie_file = os.path.join(cookie_dir, ".cookie") - try: - with open(cookie_file, 'r') as fd: - authpair = fd.read() - except IOError as err: - if 'rpcpassword' in conf: - authpair = "%s:%s" % (conf['rpcuser'], conf['rpcpassword']) - - else: - raise ValueError('Cookie file unusable (%s) and rpcpassword not specified in the configuration file: %r' % (err, btc_conf_file)) + authpair = get_authpair(conf, bitcoin.params.NAME, btc_conf_file) else: url = urlparse.urlparse(service_url) @@ -224,14 +237,8 @@ def __init__(self, self.__conn = httplib.HTTPConnection(self.__url.hostname, port=port, timeout=timeout) - def _call(self, service_name, *args): - self.__id_count += 1 - - postdata = json.dumps({'version': '1.1', - 'method': service_name, - 'params': args, - 'id': self.__id_count}) - + @property + def _headers(self): headers = { 'Host': self.__url.hostname, 'User-Agent': DEFAULT_USER_AGENT, @@ -241,7 +248,18 @@ def _call(self, service_name, *args): if self.__auth_header is not None: headers['Authorization'] = self.__auth_header - self.__conn.request('POST', self.__url.path, postdata, headers) + return headers + + def _call(self, service_name, *args): + self.__id_count += 1 + + postdata = json.dumps({'version': '1.1', + 'method': service_name, + 'params': args, + 'id': self.__id_count}) + + + self.__conn.request('POST', self.__url.path, postdata, self._headers) response = self._get_response() err = response.get('error') @@ -259,17 +277,7 @@ def _call(self, service_name, *args): def _batch(self, rpc_call_list): postdata = json.dumps(list(rpc_call_list)) - - headers = { - 'Host': self.__url.hostname, - 'User-Agent': DEFAULT_USER_AGENT, - 'Content-type': 'application/json', - } - - if self.__auth_header is not None: - headers['Authorization'] = self.__auth_header - - self.__conn.request('POST', self.__url.path, postdata, headers) + self.__conn.request('POST', self.__url.path, postdata, self._headers) return self._get_response() def _get_response(self): diff --git a/bitcoin/tests/test_rpc.py b/bitcoin/tests/test_rpc.py index 743db350..0d58cdc4 100644 --- a/bitcoin/tests/test_rpc.py +++ b/bitcoin/tests/test_rpc.py @@ -11,8 +11,38 @@ import unittest +import tempfile +from bitcoin.rpc import Proxy, parse_conf_file, get_authpair + + +class TestConfigFileparser(unittest.TestCase): + def test_parse(self): + with tempfile.TemporaryFile("w+") as fd: + fd.write(""" +datadir = /home/user/.bitcoin +# Comment +dbcache = 300 # in MB # Inline comment +""") + fd.seek(0) + self.assertEqual(parse_conf_file(fd), { + "datadir": "/home/user/.bitcoin", + "dbcache": "300" + }) + + def test_authpair_from_conf(self): + self.assertEqual( + "user:insecure_youll_be_robed", + get_authpair( + { + "rpcuser": "user", + "rpcpassword": "insecure_youll_be_robed" + }, "mainnet", "dummy.file")) + + def test_authpair_fail(self): + with self.assertRaises(ValueError): + get_authpair({}, "testnet", "ou/conf") + -from bitcoin.rpc import Proxy class Test_RPC(unittest.TestCase): # Tests disabled, see discussion below.