Bitcoin Forum
May 10, 2024, 09:23:39 AM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: [python] Bitcoin-friendly JSON-RPC library  (Read 4619 times)
jgarzik (OP)
Legendary
*
qt
Offline Offline

Activity: 1596
Merit: 1091


View Profile
April 02, 2011, 06:23:26 AM
 #1

When hacking JSON-RPC calls together for bitcoin, in python, a common solution appears to be using python-jsonrpc.  This choice is less than optimal, for [at least] two reasons:  (1) no way to use Decimal with JSON values, as is required in monetary software, and (2) does not support HTTP/1.1 persistent connections.

So, I have created the python AuthServiceProxy class specifically for bitcoin users, based on the usage model of python-jsonrpc:

     http://yyz.us/bitcoin/authproxy.py

This creates a python object whose methods are the JSON-RPC method calls.  Example:

Code:
import authproxy
import pprint

BITCOINRPC = 'http://myusername:sekritpass@127.0.0.1:8332/'
pp = pprint.PrettyPrinter(indent=4)

bitcoin = authproxy.AuthServiceProxy(BITCOINRPC)
data = bitcoin.getwork()     # call bitcoin 'getwork' RPC
pp.pprint(data)

data = bitcoin.getinfo()        # call bitcoin 'getinfo' RPC
pp.pprint(data)

This should be all that is needed for python programmers to use bitcoin's unique flavor of JSON-RPC over http or https.

In the latter 'getinfo' case, you can see that Decimal values are properly returned, rather than floats, as recently discussed in this thread:

Code:
{   'balance': Decimal('1.10000000'),
    'blocks': 116260,
    'connections': 48,
    'difficulty': Decimal('68978.89245792'),
    'errors': '',
    'generate': False,
    'genproclimit': 1,
    'hashespersec': 0,
    'keypoololdest': 1296778330,
    'paytxfee': Decimal('0.01000000'),
    'proxy': '',
    'testnet': False,
    'version': 32100}

Jeff Garzik, Bloq CEO, former bitcoin core dev team; opinions are my own.
Visit bloq.com / metronome.io
Donations / tip jar: 1BrufViLKnSWtuWGkryPsKsxonV2NQ7Tcj
1715333019
Hero Member
*
Offline Offline

Posts: 1715333019

View Profile Personal Message (Offline)

Ignore
1715333019
Reply with quote  #2

1715333019
Report to moderator
Bitcoin addresses contain a checksum, so it is very unlikely that mistyping an address will cause you to lose money.
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
1715333019
Hero Member
*
Offline Offline

Posts: 1715333019

View Profile Personal Message (Offline)

Ignore
1715333019
Reply with quote  #2

1715333019
Report to moderator
1715333019
Hero Member
*
Offline Offline

Posts: 1715333019

View Profile Personal Message (Offline)

Ignore
1715333019
Reply with quote  #2

1715333019
Report to moderator
Luke-Jr
Legendary
*
expert
Offline Offline

Activity: 2576
Merit: 1186



View Profile
April 02, 2011, 02:35:03 PM
 #2

The HTTP/1.1 improvements aside... the use of Decimal is in fact a regression. Here's why:

It will encourage people to treat bitcoin values as BTC internally, rather than atomic units. Besides being simply wrong, this also creates a problem because according to the current JSON-RPC rules, one end is allowed to send the value "0.999999999" and the other end should process it as 100000000 units (eg, 1 BTC).

Whether the library returns float or Decimal, the application still needs to process all amounts with round(1e8 * amount). Float is more efficient, and clearly discourages bad behaviour, so is a better fit in this particular case.

genjix
Legendary
*
expert
Offline Offline

Activity: 1232
Merit: 1076


View Profile
April 02, 2011, 02:52:37 PM
 #3

The class on the wiki already supports automatic introspection and uses python's Decimal class:
https://en.bitcoin.it/wiki/API_reference_%28JSON-RPC%29#Python

Code:
import urllib
import decimal
import json
 
class JSONRPCException(Exception):
    def __init__(self, rpcError):
        Exception.__init__(self)
        self.error = rpcError
 
class ServiceProxy(object):
    def __init__(self, serviceURL, serviceName=None):
        self.__serviceURL = serviceURL
        self.__serviceName = serviceName
 
    def __getattr__(self, name):
        if self.__serviceName != None:
            name = "%s.%s" % (self.__serviceName, name)
        return ServiceProxy(self.__serviceURL, name)
 
    def __call__(self, *args):
         postdata = json.dumps({"method": self.__serviceName, 'params': args, 'id':'jsonrpc'})
         respdata = urllib.urlopen(self.__serviceURL, postdata).read()
         resp = json.loads(respdata, parse_float=decimal.Decimal)
         if resp['error'] != None:
             raise JSONRPCException(resp['error'])
         else:
             return resp['result']

How does this improve on that? I saw that you're using httplib instead of urllib. Any reasons for that?

Also you may wish to read,
http://www.python.org/dev/peps/pep-0008/

Python libraries generally follow that style as to provide a consistent API. It doesn't always happen but everybody tries to use that same standard Smiley

Using the ID count is clever. Would it maybe be a good idea to either make that a class variable? For when people instantiate multiple Proxies across threads or something.
jgarzik (OP)
Legendary
*
qt
Offline Offline

Activity: 1596
Merit: 1091


View Profile
April 02, 2011, 04:00:49 PM
 #4

The class on the wiki already supports automatic introspection and uses python's Decimal class:
[...]
How does this improve on that? I saw that you're using httplib instead of urllib. Any reasons for that?

There is a list in the source code and top post that answers these questions...


Jeff Garzik, Bloq CEO, former bitcoin core dev team; opinions are my own.
Visit bloq.com / metronome.io
Donations / tip jar: 1BrufViLKnSWtuWGkryPsKsxonV2NQ7Tcj
jgarzik (OP)
Legendary
*
qt
Offline Offline

Activity: 1596
Merit: 1091


View Profile
April 02, 2011, 04:22:59 PM
 #5

Using the ID count is clever. Would it maybe be a good idea to either make that a class variable? For when people instantiate multiple Proxies across threads or something.

ID is unique per connection, and multiple threads / instances should use their own id counter.  It is used to match query and response.


Jeff Garzik, Bloq CEO, former bitcoin core dev team; opinions are my own.
Visit bloq.com / metronome.io
Donations / tip jar: 1BrufViLKnSWtuWGkryPsKsxonV2NQ7Tcj
genjix
Legendary
*
expert
Offline Offline

Activity: 1232
Merit: 1076


View Profile
April 02, 2011, 04:26:22 PM
 #6

Yep, I meant along the lines of: increment class counter, store it as private ID.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!