Index: net/Makefile =================================================================== --- net/Makefile +++ net/Makefile @@ -950,6 +950,7 @@ SUBDIR += py-pysendfile SUBDIR += py-pysocks SUBDIR += py-pysphere + SUBDIR += py-python-bitcoinrpc SUBDIR += py-pyvmomi SUBDIR += py-pyzmq SUBDIR += py-qt4-network Index: net/py-python-bitcoinrpc/Makefile =================================================================== --- /dev/null +++ net/py-python-bitcoinrpc/Makefile @@ -0,0 +1,24 @@ +# Created by: loader +# $FreeBSD$ + +PORTNAME= python-bitcoinrpc +PORTVERSION= 0.3 +DISTVERSIONPREFIX= v +CATEGORIES= net python +PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX} + +MAINTAINER= loader@FreeBSD.org +COMMENT= Enhanced version of python-jsonrpc for use with Bitcoin + +LICENSE= LGPL20 + +USE_GITHUB= yes +GH_ACCOUNT= jgarzik + +USES= python +USE_PYTHON= autoplist distutils + +post-patch: + @${TOUCH} ${WRKSRC}/bitcoinrpc/__init__.py + +.include Index: net/py-python-bitcoinrpc/distinfo =================================================================== --- /dev/null +++ net/py-python-bitcoinrpc/distinfo @@ -0,0 +1,2 @@ +SHA256 (jgarzik-python-bitcoinrpc-v0.3_GH0.tar.gz) = 1970c46b022d4352ce81d2c823d5f0671db2d1d06a1de53e5ec8cca7a53ae595 +SIZE (jgarzik-python-bitcoinrpc-v0.3_GH0.tar.gz) = 2042 Index: net/py-python-bitcoinrpc/files/patch-f607fe7 =================================================================== --- /dev/null +++ net/py-python-bitcoinrpc/files/patch-f607fe7 @@ -0,0 +1,413 @@ +--- .gitignore.orig 2015-08-06 06:33:27 UTC ++++ .gitignore +@@ -0,0 +1,4 @@ ++*.pyc ++ ++MANIFEST ++dist/ +--- README.orig 2011-04-06 19:53:31 UTC ++++ README +@@ -1,4 +1,3 @@ +- + AuthServiceProxy is an improved version of python-jsonrpc. + + It includes the following generic improvements: +@@ -7,9 +6,51 @@ It includes the following generic improv + - sends protocol 'version', per JSON-RPC 1.1 + - sends proper, incrementing 'id' + - uses standard Python json lib ++- can optionally log all RPC calls and results ++- JSON-2.0 batch support + + It also includes the following bitcoin-specific details: + + - sends Basic HTTP authentication headers +-- parses all JSON numbers that look like floats as Decimal ++- parses all JSON numbers that look like floats as Decimal, ++ and serializes Decimal values to JSON-RPC connections. ++ ++Installation: ++ ++- change the first line of setup.py to point to the directory of your installation of python 2.* ++- run setup.py ++ ++Note: This will only install bitcoinrpc. If you also want to install jsonrpc to preserve ++backwards compatibility, you have to replace 'bitcoinrpc' with 'jsonrpc' in setup.py and run it again. ++ ++Example usage: ++ ++ from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException ++ ++ # rpc_user and rpc_password are set in the bitcoin.conf file ++ rpc_connection = AuthServiceProxy("http://%s:%s@127.0.0.1:8332"%(rpc_user, rpc_password)) ++ best_block_hash = rpc_connection.getbestblockhash() ++ print(rpc_connection.getblock(best_block_hash)) + ++ # batch support : print timestamps of blocks 0 to 99 in 2 RPC round-trips: ++ commands = [ [ "getblockhash", height] for height in range(100) ] ++ block_hashes = rpc_connection.batch_(commands) ++ blocks = rpc_connection.batch_([ [ "getblock", h ] for h in block_hashes ]) ++ block_times = [ block["time"] for block in blocks ] ++ print(block_times) ++ ++Logging all RPC calls to stderr: ++ ++ from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException ++ import logging ++ ++ logging.basicConfig() ++ logging.getLogger("BitcoinRPC").setLevel(logging.DEBUG) ++ ++ rpc_connection = AuthServiceProxy("http://%s:%s@127.0.0.1:8332"%(rpc_user, rpc_password)) ++ print(rpc_connection.getinfo()) ++ ++Produces output on stderr like: ++ ++ DEBUG:BitcoinRPC:-1-> getinfo [] ++ DEBUG:BitcoinRPC:<-1- {"connections": 8, ...etc } +--- bitcoinrpc/.gitignore.orig 2015-08-06 06:33:27 UTC ++++ bitcoinrpc/.gitignore +@@ -0,0 +1,2 @@ ++*.pyc ++ +--- bitcoinrpc/authproxy.py.orig 2015-08-06 06:33:27 UTC ++++ bitcoinrpc/authproxy.py +@@ -0,0 +1,187 @@ ++ ++""" ++ Copyright 2011 Jeff Garzik ++ ++ AuthServiceProxy has the following improvements over python-jsonrpc's ++ ServiceProxy class: ++ ++ - HTTP connections persist for the life of the AuthServiceProxy object ++ (if server supports HTTP/1.1) ++ - sends protocol 'version', per JSON-RPC 1.1 ++ - sends proper, incrementing 'id' ++ - sends Basic HTTP authentication headers ++ - parses all JSON numbers that look like floats as Decimal ++ - uses standard Python json lib ++ ++ Previous copyright, from python-jsonrpc/jsonrpc/proxy.py: ++ ++ Copyright (c) 2007 Jan-Klaas Kollhof ++ ++ This file is part of jsonrpc. ++ ++ jsonrpc is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published by ++ the Free Software Foundation; either version 2.1 of the License, or ++ (at your option) any later version. ++ ++ This software is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this software; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++""" ++ ++try: ++ import http.client as httplib ++except ImportError: ++ import httplib ++import base64 ++import decimal ++import json ++import logging ++try: ++ import urllib.parse as urlparse ++except ImportError: ++ import urlparse ++ ++USER_AGENT = "AuthServiceProxy/0.1" ++ ++HTTP_TIMEOUT = 30 ++ ++log = logging.getLogger("BitcoinRPC") ++ ++class JSONRPCException(Exception): ++ def __init__(self, rpc_error): ++ parent_args = [] ++ try: ++ parent_args.append(rpc_error['message']) ++ except: ++ pass ++ Exception.__init__(self, *parent_args) ++ self.error = rpc_error ++ self.code = rpc_error['code'] if 'code' in rpc_error else None ++ self.message = rpc_error['message'] if 'message' in rpc_error else None ++ ++ def __str__(self): ++ return '%d: %s' % (self.code, self.message) ++ ++ def __repr__(self): ++ return '<%s \'%s\'>' % (self.__class__.__name__, self) ++ ++ ++def EncodeDecimal(o): ++ if isinstance(o, decimal.Decimal): ++ return round(o, 8) ++ raise TypeError(repr(o) + " is not JSON serializable") ++ ++class AuthServiceProxy(object): ++ __id_count = 0 ++ ++ def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None): ++ self.__service_url = service_url ++ self.__service_name = service_name ++ self.__url = urlparse.urlparse(service_url) ++ if self.__url.port is None: ++ port = 80 ++ else: ++ port = self.__url.port ++ (user, passwd) = (self.__url.username, self.__url.password) ++ try: ++ user = user.encode('utf8') ++ except AttributeError: ++ pass ++ try: ++ passwd = passwd.encode('utf8') ++ except AttributeError: ++ pass ++ authpair = user + b':' + passwd ++ self.__auth_header = b'Basic ' + base64.b64encode(authpair) ++ ++ if connection: ++ # Callables re-use the connection of the original proxy ++ self.__conn = connection ++ elif self.__url.scheme == 'https': ++ self.__conn = httplib.HTTPSConnection(self.__url.hostname, port, ++ timeout=timeout) ++ else: ++ self.__conn = httplib.HTTPConnection(self.__url.hostname, port, ++ timeout=timeout) ++ ++ def __getattr__(self, name): ++ if name.startswith('__') and name.endswith('__'): ++ # Python internal stuff ++ raise AttributeError ++ if self.__service_name is not None: ++ name = "%s.%s" % (self.__service_name, name) ++ return AuthServiceProxy(self.__service_url, name, connection=self.__conn) ++ ++ def __call__(self, *args): ++ AuthServiceProxy.__id_count += 1 ++ ++ log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self.__service_name, ++ json.dumps(args, default=EncodeDecimal))) ++ postdata = json.dumps({'version': '1.1', ++ 'method': self.__service_name, ++ 'params': args, ++ 'id': AuthServiceProxy.__id_count}, default=EncodeDecimal) ++ self.__conn.request('POST', self.__url.path, postdata, ++ {'Host': self.__url.hostname, ++ 'User-Agent': USER_AGENT, ++ 'Authorization': self.__auth_header, ++ 'Content-type': 'application/json'}) ++ ++ response = self._get_response() ++ if response['error'] is not None: ++ raise JSONRPCException(response['error']) ++ elif 'result' not in response: ++ raise JSONRPCException({ ++ 'code': -343, 'message': 'missing JSON-RPC result'}) ++ else: ++ return response['result'] ++ ++ def batch_(self, rpc_calls): ++ """Batch RPC call. ++ Pass array of arrays: [ [ "method", params... ], ... ] ++ Returns array of results. ++ """ ++ batch_data = [] ++ for rpc_call in rpc_calls: ++ AuthServiceProxy.__id_count += 1 ++ m = rpc_call.pop(0) ++ batch_data.append({"jsonrpc":"2.0", "method":m, "params":rpc_call, "id":AuthServiceProxy.__id_count}) ++ ++ postdata = json.dumps(batch_data, default=EncodeDecimal) ++ log.debug("--> "+postdata) ++ self.__conn.request('POST', self.__url.path, postdata, ++ {'Host': self.__url.hostname, ++ 'User-Agent': USER_AGENT, ++ 'Authorization': self.__auth_header, ++ 'Content-type': 'application/json'}) ++ results = [] ++ responses = self._get_response() ++ for response in responses: ++ if response['error'] is not None: ++ raise JSONRPCException(response['error']) ++ elif 'result' not in response: ++ raise JSONRPCException({ ++ 'code': -343, 'message': 'missing JSON-RPC result'}) ++ else: ++ results.append(response['result']) ++ return results ++ ++ def _get_response(self): ++ http_response = self.__conn.getresponse() ++ if http_response is None: ++ raise JSONRPCException({ ++ 'code': -342, 'message': 'missing HTTP response from server'}) ++ ++ responsedata = http_response.read().decode('utf8') ++ response = json.loads(responsedata, parse_float=decimal.Decimal) ++ if "error" in response and response["error"] is None: ++ log.debug("<-%s- %s"%(response["id"], json.dumps(response["result"], default=EncodeDecimal))) ++ else: ++ log.debug("<-- "+responsedata) ++ return response +--- jsonrpc/__init__.py.orig 2011-04-06 19:53:31 UTC ++++ jsonrpc/__init__.py +@@ -1,2 +1,2 @@ +-from jsonrpc.json import loads, dumps, JSONEncodeException, JSONDecodeException ++from .json import loads, dumps, JSONEncodeException, JSONDecodeException + from jsonrpc.proxy import ServiceProxy, JSONRPCException +--- jsonrpc/authproxy.py.orig 2011-04-06 19:53:31 UTC ++++ jsonrpc/authproxy.py +@@ -1,100 +1,3 @@ ++from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException + +-""" +- Copyright 2011 Jeff Garzik +- +- AuthServiceProxy has the following improvements over python-jsonrpc's +- ServiceProxy class: +- +- - HTTP connections persist for the life of the AuthServiceProxy object +- (if server supports HTTP/1.1) +- - sends protocol 'version', per JSON-RPC 1.1 +- - sends proper, incrementing 'id' +- - sends Basic HTTP authentication headers +- - parses all JSON numbers that look like floats as Decimal +- - uses standard Python json lib +- +- Previous copyright, from python-jsonrpc/jsonrpc/proxy.py: +- +- Copyright (c) 2007 Jan-Klaas Kollhof +- +- This file is part of jsonrpc. +- +- jsonrpc is free software; you can redistribute it and/or modify +- it under the terms of the GNU Lesser General Public License as published by +- the Free Software Foundation; either version 2.1 of the License, or +- (at your option) any later version. +- +- This software is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU Lesser General Public License for more details. +- +- You should have received a copy of the GNU Lesser General Public License +- along with this software; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-""" +- +-import httplib +-import base64 +-import json +-import decimal +-import urlparse +- +-USER_AGENT = "AuthServiceProxy/0.1" +- +-HTTP_TIMEOUT = 30 +- +-class JSONRPCException(Exception): +- def __init__(self, rpcError): +- Exception.__init__(self) +- self.error = rpcError +- +-class AuthServiceProxy(object): +- def __init__(self, serviceURL, serviceName=None): +- self.__serviceURL = serviceURL +- self.__serviceName = serviceName +- self.__url = urlparse.urlparse(serviceURL) +- if self.__url.port is None: +- port = 80 +- else: +- port = self.__url.port +- self.__idcnt = 0 +- authpair = "%s:%s" % (self.__url.username, self.__url.password) +- self.__authhdr = "Basic %s" % (base64.b64encode(authpair)) +- self.__conn = httplib.HTTPConnection(self.__url.hostname, port, False, +- HTTP_TIMEOUT) +- +- def __getattr__(self, name): +- if self.__serviceName != None: +- name = "%s.%s" % (self.__serviceName, name) +- return AuthServiceProxy(self.__serviceURL, name) +- +- def __call__(self, *args): +- self.__idcnt += 1 +- +- postdata = json.dumps({ +- 'version': '1.1', +- 'method': self.__serviceName, +- 'params': args, +- 'id': self.__idcnt}) +- self.__conn.request('POST', self.__url.path, postdata, +- { 'Host' : self.__url.hostname, +- 'User-Agent' : USER_AGENT, +- 'Authorization' : self.__authhdr, +- 'Content-type' : 'application/json' }) +- +- httpresp = self.__conn.getresponse() +- if httpresp is None: +- raise JSONRPCException({ +- 'code' : -342, 'message' : 'missing HTTP response from server'}) +- +- resp = json.loads(httpresp.read(), parse_float=decimal.Decimal) +- if resp['error'] != None: +- raise JSONRPCException(resp['error']) +- elif 'result' not in resp: +- raise JSONRPCException({ +- 'code' : -343, 'message' : 'missing JSON-RPC result'}) +- else: +- return resp['result'] +- +- ++__all__ = ['AuthServiceProxy', 'JSONRPCException'] +--- jsonrpc/json.py.orig 2011-04-06 19:53:31 UTC ++++ jsonrpc/json.py +@@ -1,5 +1,9 @@ +-json = __import__('json') +-loads = json.loads +-dumps = json.dumps +-JSONEncodeException = TypeError +-JSONDecodeException = ValueError ++_json = __import__('json') ++loads = _json.loads ++dumps = _json.dumps ++if hasattr(_json, 'JSONEncodeException'): ++ JSONEncodeException = _json.JSONEncodeException ++ JSONDecodeException = _json.JSONDecodeException ++else: ++ JSONEncodeException = TypeError ++ JSONDecodeException = ValueError +--- jsonrpc/proxy.py.orig 2011-04-06 19:53:31 UTC ++++ jsonrpc/proxy.py +@@ -1 +1 @@ +-from authproxy import AuthServiceProxy as ServiceProxy, JSONRPCException ++from bitcoinrpc.authproxy import AuthServiceProxy as ServiceProxy, JSONRPCException +--- setup.py.orig 2015-08-06 06:33:27 UTC ++++ setup.py +@@ -0,0 +1,15 @@ ++#!/usr/bin/env python ++ ++from distutils.core import setup ++ ++setup(name='python-bitcoinrpc', ++ version='0.1', ++ description='Enhanced version of python-jsonrpc for use with Bitcoin', ++ long_description=open('README').read(), ++ author='Jeff Garzik', ++ author_email='', ++ maintainer='Jeff Garzik', ++ maintainer_email='', ++ url='http://www.github.com/jgarzik/python-bitcoinrpc', ++ packages=['bitcoinrpc'], ++ classifiers=['License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Operating System :: OS Independent']) Index: net/py-python-bitcoinrpc/pkg-descr =================================================================== --- /dev/null +++ net/py-python-bitcoinrpc/pkg-descr @@ -0,0 +1,18 @@ +AuthServiceProxy is an improved version of python-jsonrpc. + +It includes the following generic improvements: + +- HTTP connections persist for the life of the AuthServiceProxy object +- sends protocol 'version', per JSON-RPC 1.1 +- sends proper, incrementing 'id' +- uses standard Python json lib +- can optionally log all RPC calls and results +- JSON-2.0 batch support + +It also includes the following bitcoin-specific details: + +- sends Basic HTTP authentication headers +- parses all JSON numbers that look like floats as Decimal, + and serializes Decimal values to JSON-RPC connections. + +WWW: https://github.com/jgarzik/python-bitcoinrpc