Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and some test for RPC #255

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bitcoin/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
104 changes: 56 additions & 48 deletions bitcoin/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -148,28 +186,15 @@ 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': ""}

# 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
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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')
Expand All @@ -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):
Expand Down
32 changes: 31 additions & 1 deletion bitcoin/tests/test_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down