Skip to content

Commit bc5400c

Browse files
committed
Rename and document hexlify/unhexlify wrappers
These wrapper work differently than functions from binascii, even though they have the same name. That could be a source of confusion. In this commit, the functions are renamed to make it clear that they operate with strings. Because they cannot handle Unicode in any meaningful way, I have changed their implementation to not even attempt it, as it is more complicated to handle Unicode, and it doesn't even work anyway.
1 parent 0d1960e commit bc5400c

File tree

1 file changed

+44
-17
lines changed

1 file changed

+44
-17
lines changed

bitcoin/rpc.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,35 @@
4141

4242
DEFAULT_HTTP_TIMEOUT = 30
4343

44-
unhexlify = lambda h: binascii.unhexlify(h.encode('utf8'))
45-
hexlify = lambda b: binascii.hexlify(b).decode('utf8')
44+
def unhexlify_str(h):
45+
"""
46+
Converts a string containing hexadecimal encoding into a bytes-object.
47+
48+
It works by encoding the given string h as ASCII, then interpreting each of
49+
its two-character ASCII tuples as bytes:
50+
- "00" to byte 0
51+
- "ff" to byte 255
52+
- "FF" also to byte 255
53+
54+
The string must only contain characters in the ranges 0-9, a-f and A-F.
55+
56+
If the string contains characters not in ASCII,
57+
UnicodeEncodeError is raised.
58+
If the string contains out-of-range ASCII characters,
59+
binascii.Error is raised.
60+
If number of encoded ASCII bytes is odd,
61+
binascii.Error is raised.
62+
"""
63+
return binascii.unhexlify(h.encode('ascii'))
64+
65+
def hexlify_str(b):
66+
"""
67+
Given an arbitrary bytes-like object, returns a string (that would encode
68+
as ASCII) containing the hex-representation of the bytes-like object.
69+
70+
Always succeeds.
71+
"""
72+
return binascii.hexlify(b).decode('ascii')
4673

4774

4875
class JSONRPCError(Exception):
@@ -365,10 +392,10 @@ def fundrawtransaction(self, tx, include_watching=False):
365392
'changepos': Position of added change output, or -1,
366393
}
367394
"""
368-
hextx = hexlify(tx.serialize())
395+
hextx = hexlify_str(tx.serialize())
369396
r = self._call('fundrawtransaction', hextx, include_watching)
370397

371-
r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
398+
r['tx'] = CTransaction.deserialize(unhexlify_str(r['hex']))
372399
del r['hex']
373400

374401
r['fee'] = int(r['fee'] * COIN)
@@ -456,7 +483,7 @@ def getblockheader(self, block_hash, verbose=False):
456483
'nextblockhash':nextblockhash,
457484
'chainwork':x(r['chainwork'])}
458485
else:
459-
return CBlockHeader.deserialize(unhexlify(r))
486+
return CBlockHeader.deserialize(unhexlify_str(r))
460487

461488

462489
def getblock(self, block_hash):
@@ -477,7 +504,7 @@ def getblock(self, block_hash):
477504
except InvalidAddressOrKeyError as ex:
478505
raise IndexError('%s.getblock(): %s (%d)' %
479506
(self.__class__.__name__, ex.error['message'], ex.error['code']))
480-
return CBlock.deserialize(unhexlify(r))
507+
return CBlock.deserialize(unhexlify_str(r))
481508

482509
def getblockcount(self):
483510
"""Return the number of blocks in the longest block chain"""
@@ -556,7 +583,7 @@ def getrawtransaction(self, txid, verbose=False):
556583
raise IndexError('%s.getrawtransaction(): %s (%d)' %
557584
(self.__class__.__name__, ex.error['message'], ex.error['code']))
558585
if verbose:
559-
r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
586+
r['tx'] = CTransaction.deserialize(unhexlify_str(r['hex']))
560587
del r['hex']
561588
del r['txid']
562589
del r['version']
@@ -565,7 +592,7 @@ def getrawtransaction(self, txid, verbose=False):
565592
del r['vout']
566593
r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None
567594
else:
568-
r = CTransaction.deserialize(unhexlify(r))
595+
r = CTransaction.deserialize(unhexlify_str(r))
569596

570597
return r
571598

@@ -613,7 +640,7 @@ def gettxout(self, outpoint, includemempool=True):
613640
raise IndexError('%s.gettxout(): unspent txout %r not found' % (self.__class__.__name__, outpoint))
614641

615642
r['txout'] = CTxOut(int(r['value'] * COIN),
616-
CScript(unhexlify(r['scriptPubKey']['hex'])))
643+
CScript(unhexlify_str(r['scriptPubKey']['hex'])))
617644
del r['value']
618645
del r['scriptPubKey']
619646
r['bestblock'] = lx(r['bestblock'])
@@ -653,7 +680,7 @@ def listunspent(self, minconf=0, maxconf=9999999, addrs=None):
653680
unspent['address'] = CBitcoinAddress(unspent['address'])
654681
except KeyError:
655682
pass
656-
unspent['scriptPubKey'] = CScript(unhexlify(unspent['scriptPubKey']))
683+
unspent['scriptPubKey'] = CScript(unhexlify_str(unspent['scriptPubKey']))
657684
unspent['amount'] = int(unspent['amount'] * COIN)
658685
r2.append(unspent)
659686
return r2
@@ -669,7 +696,7 @@ def sendrawtransaction(self, tx, allowhighfees=False):
669696
670697
allowhighfees - Allow even if fees are unreasonably high.
671698
"""
672-
hextx = hexlify(tx.serialize())
699+
hextx = hexlify_str(tx.serialize())
673700
r = None
674701
if allowhighfees:
675702
r = self._call('sendrawtransaction', hextx, True)
@@ -699,9 +726,9 @@ def signrawtransaction(self, tx, *args):
699726
700727
FIXME: implement options
701728
"""
702-
hextx = hexlify(tx.serialize())
729+
hextx = hexlify_str(tx.serialize())
703730
r = self._call('signrawtransaction', hextx, *args)
704-
r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
731+
r['tx'] = CTransaction.deserialize(unhexlify_str(r['hex']))
705732
del r['hex']
706733
return r
707734

@@ -711,9 +738,9 @@ def signrawtransactionwithwallet(self, tx, *args):
711738
712739
FIXME: implement options
713740
"""
714-
hextx = hexlify(tx.serialize())
741+
hextx = hexlify_str(tx.serialize())
715742
r = self._call('signrawtransactionwithwallet', hextx, *args)
716-
r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
743+
r['tx'] = CTransaction.deserialize(unhexlify_str(r['hex']))
717744
del r['hex']
718745
return r
719746

@@ -723,7 +750,7 @@ def submitblock(self, block, params=None):
723750
params is optional and is currently ignored by bitcoind. See
724751
https://en.bitcoin.it/wiki/BIP_0022 for full specification.
725752
"""
726-
hexblock = hexlify(block.serialize())
753+
hexblock = hexlify_str(block.serialize())
727754
if params is not None:
728755
return self._call('submitblock', hexblock, params)
729756
else:
@@ -735,7 +762,7 @@ def validateaddress(self, address):
735762
if r['isvalid']:
736763
r['address'] = CBitcoinAddress(r['address'])
737764
if 'pubkey' in r:
738-
r['pubkey'] = unhexlify(r['pubkey'])
765+
r['pubkey'] = unhexlify_str(r['pubkey'])
739766
return r
740767

741768
def unlockwallet(self, password, timeout=60):

0 commit comments

Comments
 (0)