diff --git a/COPYING b/COPYING index 1fb429f..fec0301 100644 --- a/COPYING +++ b/COPYING @@ -1,3 +1,6 @@ +With the exception of bitcoin/rpc.py, which is licensed under the GNU LGPL, the +following license terms apply to the package python-bitcoinlib unless otherwise +noted: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README b/README index 063fc4b..72c990e 100644 --- a/README +++ b/README @@ -1,9 +1,5 @@ +Peter Todd has taken over maintainership of python-bitcoinlib. Please use the +new repository at: -This python library provides an easy interface to the bitcoin -data structures and protocol. +https://github.com/petertodd/python-bitcoinlib - -Unit tests ----------- - -python -m unittest discover diff --git a/bitcoin/__init__.py b/bitcoin/__init__.py index e69de29..27fe8df 100644 --- a/bitcoin/__init__.py +++ b/bitcoin/__init__.py @@ -0,0 +1,4 @@ +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from __future__ import absolute_import, division, print_function, unicode_literals diff --git a/bitcoin/base58.py b/bitcoin/base58.py index 20d5a75..855f8bf 100644 --- a/bitcoin/base58.py +++ b/bitcoin/base58.py @@ -8,6 +8,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from __future__ import absolute_import, division, print_function, unicode_literals + from bitcoin.serialize import Hash, ser_uint256 b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @@ -24,7 +26,7 @@ def encode(b): """Encode bytes to a base58-encoded string""" # Convert big-endian bytes to integer - n = int('0x0' + hexlify(b), 16) + n = int('0x0' + hexlify(b).decode('utf8'), 16) # Divide that integer into bas58 res = [] @@ -34,9 +36,14 @@ def encode(b): res = ''.join(res[::-1]) # Encode leading zeros as base58 zeros + import sys + czero = b'\x00' + if sys.version > '3': + # In Python3 indexing a bytes returns numbers, not characters. + czero = 0 pad = 0 for c in b: - if c == chr(0): pad += 1 + if c == czero: pad += 1 else: break return b58_digits[0] * pad + res @@ -58,7 +65,7 @@ def decode(s): h = '%x' % n if len(h) % 2: h = '0' + h - res = unhexlify(h) + res = unhexlify(h.encode('utf8')) # Add padding back. pad = 0 @@ -77,6 +84,9 @@ def __new__(cls, data, nVersion): self.nVersion = nVersion return self + def __repr__(self): + return '%s(%s, %d)' % (self.__class__.__name__, bytes.__repr__(self), self.nVersion) + def __str__(self): vs = chr(self.nVersion) + self check = ser_uint256(Hash(vs))[0:4] diff --git a/bitcoin/bignum.py b/bitcoin/bignum.py index ed62d15..2dbe662 100644 --- a/bitcoin/bignum.py +++ b/bitcoin/bignum.py @@ -6,6 +6,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from __future__ import absolute_import, division, print_function, unicode_literals + import struct @@ -15,7 +17,7 @@ def bn_bytes(v, have_ext=False): ext = 0 if have_ext: ext = 1 - return ((v.bit_length()+7)/8) + ext + return ((v.bit_length()+7)//8) + ext def bn2bin(v): s = bytearray() @@ -26,7 +28,7 @@ def bn2bin(v): return s def bin2bn(s): - l = 0L + l = 0 for ch in s: l = (l << 8) | ch return l @@ -41,7 +43,7 @@ def bn2mpi(v): neg = True v = -v - s = struct.pack(">I", bn_bytes(v, have_ext)) + s = struct.pack(b">I", bn_bytes(v, have_ext)) ext = bytearray() if have_ext: ext.append(0) @@ -57,11 +59,11 @@ def mpi2bn(s): if len(s) < 4: return None s_size = str(s[:4]) - v_len = struct.unpack(">I", s_size)[0] + v_len = struct.unpack(b">I", s_size)[0] if len(s) != (v_len + 4): return None if v_len == 0: - return 0L + return 0 v_str = bytearray(s[4:]) neg = False @@ -87,7 +89,7 @@ def bn2vch(v): return str(mpi2vch(bn2mpi(v))) def vch2mpi(s): - r = struct.pack(">I", len(s)) # size + r = struct.pack(b">I", len(s)) # size r += s[::-1] # reverse string, converting LE->BE return r diff --git a/bitcoin/bloom.py b/bitcoin/bloom.py new file mode 100644 index 0000000..c562d08 --- /dev/null +++ b/bitcoin/bloom.py @@ -0,0 +1,105 @@ + +# +# bloom.py +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +import struct +import math +from bitcoin.serialize import * +from bitcoin.coredefs import * +from bitcoin.core import * +from bitcoin.hash import MurmurHash3 + +class CBloomFilter(object): + # 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% + MAX_BLOOM_FILTER_SIZE = 36000 + MAX_HASH_FUNCS = 50 + + UPDATE_NONE = 0 + UPDATE_ALL = 1 + UPDATE_P2PUBKEY_ONLY = 2 + UPDATE_MASK = 3 + + def __init__(self, nElements, nFPRate, nTweak, nFlags): + """Create a new bloom filter + + The filter will have a given false-positive rate when filled with the + given number of elements. + + Note that if the given parameters will result in a filter outside the + bounds of the protocol limits, the filter created will be as close to + the given parameters as possible within the protocol limits. This will + apply if nFPRate is very low or nElements is unreasonably high. + + nTweak is a constant which is added to the seed value passed to the + hash function It should generally always be a random value (and is + largely only exposed for unit testing) + + nFlags should be one of the UPDATE_* enums (but not _MASK) + """ + LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455 + LN2 = 0.6931471805599453094172321214581765680755001343602552 + self.vData = bytearray(int(min(-1 / LN2SQUARED * nElements * math.log(nFPRate), self.MAX_BLOOM_FILTER_SIZE * 8) / 8)) + self.nHashFuncs = int(min(len(self.vData) * 8 / nElements * LN2, self.MAX_HASH_FUNCS)) + self.nTweak = nTweak + self.nFlags = nFlags + + def bloom_hash(self, nHashNum, vDataToHash): + return MurmurHash3(((nHashNum * 0xFBA4C795) + self.nTweak) & 0xFFFFFFFF, vDataToHash) % (len(self.vData) * 8) + + __bit_mask = bytearray([0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]) + def insert(self, elem): + """Insert an element in the filter. + + elem may be a COutPoint or bytes + """ + if isinstance(elem, COutPoint): + elem = elem.serialize() + + if len(self.vData) == 1 and self.vData[0] == 0xff: + return + + for i in range(0, self.nHashFuncs): + nIndex = self.bloom_hash(i, elem) + # Sets bit nIndex of vData + self.vData[nIndex >> 3] |= self.__bit_mask[7 & nIndex] + + def contains(self, elem): + """Test if the filter contains an element + + elem may be a COutPoint or bytes + """ + if isinstance(elem, COutPoint): + elem = elem.serialize() + + if len(self.vData) == 1 and self.vData[0] == 0xff: + return True + + for i in range(0, self.nHashFuncs): + nIndex = self.bloom_hash(i, elem) + if not (self.vData[nIndex >> 3] & self.__bit_mask[7 & nIndex]): + return False + return True + + def IsWithinSizeConstraints(self): + return len(self.vData) <= self.MAX_BLOOM_FILTER_SIZE and self.nHashFuncs <= self.MAX_HASH_FUNCS + + def IsRelevantAndUpdate(tx, tx_hash): + # Not useful for a client, so not implemented yet. + raise NotImplementedError + + __struct = struct.Struct(b'= CADDR_TIME_VERSION: - self.nTime = struct.unpack("H", f.read(2))[0] + self.port = struct.unpack(b">H", f.read(2))[0] def serialize(self): - r = "" + r = b"" if self.protover >= CADDR_TIME_VERSION: - r += struct.pack("H", self.port) + r += struct.pack(b">H", self.port) return r def __repr__(self): return "CAddress(nTime=%d nServices=%i ip=%s port=%i)" % (self.nTime, self.nServices, self.ip, self.port) @@ -49,13 +51,13 @@ class CInv(object): 2: "Block"} def __init__(self): self.type = 0 - self.hash = 0L + self.hash = 0 def deserialize(self, f): - self.type = struct.unpack(" 1: newhashes = [] - for i in xrange(0, len(hashes), 2): + for i in range(0, len(hashes), 2): i2 = min(i+1, len(hashes)-1) newhashes.append(hashlib.sha256(hashlib.sha256(hashes[i] + hashes[i2]).digest()).digest()) hashes = newhashes @@ -293,35 +295,35 @@ def __init__(self): self.nMaxVer = 0 self.setSubVer = [] self.nPriority = 0 - self.strComment = "" - self.strStatusBar = "" - self.strReserved = "" + self.strComment = b"" + self.strStatusBar = b"" + self.strReserved = b"" def deserialize(self, f): - self.nVersion = struct.unpack(" self.checkpoint_max: self.checkpoint_max = height NETWORKS = { - 'mainnet' : NetMagic("\xf9\xbe\xb4\xd9", - 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26fL, + 'mainnet' : NetMagic(b"\xf9\xbe\xb4\xd9", + 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f, { - 0: 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26fL, - 11111: 0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1dL, - 33333: 0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6L, - 74000: 0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20L, - 105000: 0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97L, - 134444: 0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0feL, - 168000: 0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763L, - 193000: 0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317L, - 210000: 0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342eL, - 216116: 0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4eL, + 0: 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f, + 11111: 0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d, + 33333: 0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6, + 74000: 0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20, + 105000: 0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97, + 134444: 0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe, + 168000: 0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763, + 193000: 0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317, + 210000: 0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e, + 216116: 0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e, }), - 'testnet3' : NetMagic("\x0b\x11\x09\x07", - 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943L, + 'testnet3' : NetMagic(b"\x0b\x11\x09\x07", + 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943, { - 0: 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943L, + 0: 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943, }) } diff --git a/bitcoin/hash.py b/bitcoin/hash.py new file mode 100644 index 0000000..158e61a --- /dev/null +++ b/bitcoin/hash.py @@ -0,0 +1,78 @@ + +# +# hash.py +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +import struct +from bitcoin.serialize import * +from bitcoin.coredefs import * +from bitcoin.script import CScript + +def ROTL32(x, r): + assert x <= 0xFFFFFFFF + return ((x << r) & 0xFFFFFFFF) | (x >> (32 - r)) + +def MurmurHash3(nHashSeed, vDataToHash): + """MurmurHash3 (x86_32) + + Used for bloom filters. See http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + """ + + assert nHashSeed <= 0xFFFFFFFF + + h1 = nHashSeed + c1 = 0xcc9e2d51 + c2 = 0x1b873593 + + # body + i = 0 + while i < len(vDataToHash) - len(vDataToHash) % 4 \ + and len(vDataToHash) - i >= 4: + + k1 = struct.unpack(b" '3': + # In Py3 indexing bytes returns numbers, not characters + bord = lambda x: x + if len(vDataToHash) & 3 >= 3: + k1 ^= bord(vDataToHash[j+2]) << 16 + if len(vDataToHash) & 3 >= 2: + k1 ^= bord(vDataToHash[j+1]) << 8 + if len(vDataToHash) & 3 >= 1: + k1 ^= bord(vDataToHash[j]) + + k1 &= 0xFFFFFFFF + k1 = (k1 * c1) & 0xFFFFFFFF + k1 = ROTL32(k1, 15) + k1 = (k1 * c2) & 0xFFFFFFFF + h1 ^= k1 + + # finalization + h1 ^= len(vDataToHash) & 0xFFFFFFFF + h1 ^= (h1 & 0xFFFFFFFF) >> 16 + h1 *= 0x85ebca6b + h1 ^= (h1 & 0xFFFFFFFF) >> 13 + h1 *= 0xc2b2ae35 + h1 ^= (h1 & 0xFFFFFFFF) >> 16 + + return h1 & 0xFFFFFFFF diff --git a/bitcoin/key.py b/bitcoin/key.py index 3b7a3f6..28fc9de 100644 --- a/bitcoin/key.py +++ b/bitcoin/key.py @@ -42,6 +42,8 @@ def generate(self, secret=None): group = ssl.EC_KEY_get0_group(self.k) pub_key = ssl.EC_POINT_new(group) ctx = ssl.BN_CTX_new() + if not ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx): + raise ValueError("Could not derive public key from the supplied secret.") ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx) ssl.EC_KEY_set_private_key(self.k, priv_key) ssl.EC_KEY_set_public_key(self.k, pub_key) @@ -72,14 +74,15 @@ def get_pubkey(self): return mb.raw def sign(self, hash): - sig_size = ssl.ECDSA_size(self.k) - mb_sig = ctypes.create_string_buffer(sig_size) - sig_size0 = ctypes.POINTER(ctypes.c_int)() - assert 1 == ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k) - return mb_sig.raw + sig_size0 = ctypes.c_uint32() + sig_size0.value = ssl.ECDSA_size(self.k) + mb_sig = ctypes.create_string_buffer(sig_size0.value) + result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k) + assert 1 == result + return mb_sig.raw[:sig_size0.value] def verify(self, hash, sig): - return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) + return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) == 1 def set_compressed(self, compressed): if compressed: @@ -107,6 +110,10 @@ def set_compressed(self, compressed): k = CKey() k.generate (ec_secret.decode('hex')) k.set_compressed(True) - print k.get_privkey ().encode('hex') - print k.get_pubkey().encode('hex') - print k.get_secret().encode('hex') + print(k.get_privkey ().encode('hex')) + print(k.get_pubkey().encode('hex')) + # not sure this is needed any more: print k.get_secret().encode('hex') + + hash = 'Hello, world!' + print(k.verify(hash, k.sign(hash))) + diff --git a/bitcoin/messages.py b/bitcoin/messages.py index 6c88581..2b35569 100644 --- a/bitcoin/messages.py +++ b/bitcoin/messages.py @@ -6,6 +6,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from __future__ import absolute_import, division, print_function, unicode_literals + import struct import time import random @@ -17,7 +19,7 @@ MSG_BLOCK = 2 class msg_version(object): - command = "version" + command = b"version" def __init__(self, protover=PROTO_VERSION): self.protover = MIN_PROTO_VERSION self.nVersion = protover @@ -26,23 +28,23 @@ def __init__(self, protover=PROTO_VERSION): self.addrTo = CAddress(MIN_PROTO_VERSION) self.addrFrom = CAddress(MIN_PROTO_VERSION) self.nNonce = random.getrandbits(64) - self.strSubVer = '/python-bitcoin-0.0.1/' + self.strSubVer = b'/python-bitcoin-0.0.1/' self.nStartingHeight = -1 def deserialize(self, f): - self.nVersion = struct.unpack("= 106: self.addrFrom = CAddress(MIN_PROTO_VERSION) self.addrFrom.deserialize(f) - self.nNonce = struct.unpack("= 209: - self.nStartingHeight = struct.unpack(" BIP0031_VERSION: - self.nonce = struct.unpack(" BIP0031_VERSION: - r += struct.pack(" rather than > + f.__name__ = name + return f + + + def _batch(self, rpc_call_list): + postdata = json.dumps(list(rpc_call_list)) + self.__conn.request('POST', self.__url.path, postdata, + {'Host': self.__url.hostname, + 'User-Agent': USER_AGENT, + 'Authorization': self.__auth_header, + 'Content-type': 'application/json'}) + + return self._get_response() + + def _get_response(self): + http_response = self.__conn.getresponse() + if http_response is None: + raise JSONRPCException({ + 'code': -342, 'message': 'missing HTTP response from server'}) + + return json.loads(http_response.read().decode('utf8'), + parse_float=decimal.Decimal) + + +class Proxy(RawProxy): + def __init__(self, service_url=None, + service_port=8332, + btc_conf_file=None, + timeout=HTTP_TIMEOUT, + **kwargs): + """Create a proxy to a bitcoin RPC service + + Unlike RawProxy data is passed as objects, rather than JSON. (not yet + fully implemented) + + If service_url is not specified the username and password are read out + of the file btc_conf_file. If btc_conf_file is not specified + ~/.bitcoin/bitcoin.conf or equivalent is used by default. + + Usually no arguments to Proxy() are needed; the local bitcoind will be + used. + + timeout - timeout in seconds before the HTTP interface times out + """ + super(Proxy, self).__init__(service_url=service_url, service_port=service_port, btc_conf_file=btc_conf_file, + timeout=HTTP_TIMEOUT, + **kwargs) + + def getinfo(self): + """Returns an object containing various state info""" + r = self._call('getinfo') + r['balance'] = int(r['balance'] * COIN) + r['paytxfee'] = int(r['paytxfee'] * COIN) + return r + + def getnewaddress(self, account=None): + """Return a new Bitcoin address for receiving payments. + + If account is not None, it is added to the address book so payments + received with the address will be credited to account. + """ + r = None + if account is not None: + r = self._call('getnewaddress', account) + else: + r = self._call('getnewaddress') + + return CBitcoinAddress.from_str(r) + + def getaccountaddress(self, account=None): + """Return the current Bitcoin address for receiving payments to this account.""" + r = self._call('getaccountaddress', account) + return CBitcoinAddress.from_str(r) + + def validateaddress(self, address): + """Return information about an address""" + r = self._call('validateaddress', str(address)) + r['address'] = CBitcoinAddress.from_str(r['address']) + return r diff --git a/bitcoin/script.py b/bitcoin/script.py index 88721d2..da27059 100644 --- a/bitcoin/script.py +++ b/bitcoin/script.py @@ -6,6 +6,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from __future__ import absolute_import, division, print_function, unicode_literals + import struct SIGHASH_ALL = 1 @@ -462,14 +464,14 @@ def getop(self): s = self.getchars(2) if s is None: return False - datasize = struct.unpack("= len(template): - return None + if i >= len(template): + return None if not self.getop(): return None - - expected_op = template[i] - if expected_op == OP_PUBKEYHASH or expected_op == OP_PUBKEY: - if self.sop.op > OP_PUSHDATA4: - return None - l.append(self.sop.data) - elif self.sop.op != expected_op: - return None + expected_op = template[i] + if expected_op == OP_PUBKEYHASH or expected_op == OP_PUBKEY: + if self.sop.op > OP_PUSHDATA4: + return None + l.append(self.sop.data) + + elif self.sop.op != expected_op: + return None - i += 1 + i += 1 return l def match_alltemp(self, vch_in=None): for temp in TEMPLATES: - l = self.match_temp(temp, vch_in) - if l is not None: - return l - return None + l = self.match_temp(temp, vch_in) + if l is not None: + return l + return None def __repr__(self): return "CScript(vchsz %d)" % (len(self.vch),) diff --git a/bitcoin/scripteval.py b/bitcoin/scripteval.py index d65c73e..f104920 100644 --- a/bitcoin/scripteval.py +++ b/bitcoin/scripteval.py @@ -6,6 +6,12 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from __future__ import absolute_import, division, print_function, unicode_literals + +import sys +if sys.version > '3': + long = int + import hashlib from bitcoin.serialize import Hash, Hash160, ser_uint256, ser_uint160 from bitcoin.script import * @@ -15,33 +21,33 @@ def SignatureHash(script, txTo, inIdx, hashtype): if inIdx >= len(txTo.vin): - return (0L, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) + return (1, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) txtmp = CTransaction() txtmp.copy(txTo) for txin in txtmp.vin: - txin.scriptSig = '' + txin.scriptSig = b'' txtmp.vin[inIdx].scriptSig = script.vch if (hashtype & 0x1f) == SIGHASH_NONE: txtmp.vout = [] - for i in xrange(len(txtmp.vin)): + for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 elif (hashtype & 0x1f) == SIGHASH_SINGLE: outIdx = inIdx if outIdx >= len(txtmp.vout): - return (0L, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) + return (1, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) tmp = txtmp.vout[outIdx] txtmp.vout = [] - for i in xrange(outIdx): + for i in range(outIdx): txtmp.vout.append(CTxOut()) txtmp.vout.append(tmp) - for i in xrange(len(txtmp.vin)): + for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 @@ -51,7 +57,7 @@ def SignatureHash(script, txTo, inIdx, hashtype): txtmp.vin.append(tmp) s = txtmp.serialize() - s += struct.pack("> bn2 elif opcode == OP_BOOLAND: - bn = long(bn1 != 0L and bn2 != 0L) + bn = long(bn1 != 0 and bn2 != 0) elif opcode == OP_BOOLOR: - bn = long(bn1 != 0L or bn2 != 0L) + bn = long(bn1 != 0 or bn2 != 0) elif opcode == OP_NUMEQUAL or opcode == OP_NUMEQUALVERIFY: bn = long(bn1 == bn2) @@ -374,11 +378,11 @@ def EvalScript(stack, scriptIn, txTo, inIdx, hashtype): txTo, inIdx, hashtype) if ok: if sop.op != OP_CHECKSIGVERIFY: - stack.append("\x01") + stack.append(b"\x01") else: if sop.op == OP_CHECKSIGVERIFY: return False - stack.append("\x00") + stack.append(b"\x00") elif fExec and sop.op == OP_CODESEPARATOR: script.pbegincodehash = script.pc @@ -416,9 +420,9 @@ def EvalScript(stack, scriptIn, txTo, inIdx, hashtype): is_equal = (v1 == v2) if is_equal: - stack.append("\x01") + stack.append(b"\x01") else: - stack.append("\x00") + stack.append(b"\x00") if sop.op == OP_EQUALVERIFY: if is_equal: @@ -556,9 +560,9 @@ def EvalScript(stack, scriptIn, txTo, inIdx, hashtype): bn1 = CastToBigNum(stack.pop()) v = (bn2 <= bn1) and (bn1 < bn3) if v: - stack.append("\x01") + stack.append(b"\x01") else: - stack.append("\x00") + stack.append(b"\x00") elif fExec: #print("Unsupported opcode", OPCODE_NAMES[sop.op]) @@ -571,7 +575,7 @@ def CastToBigNum(s): return v def CastToBool(s): - for i in xrange(len(s)): + for i in range(len(s)): sv = ord(s[i]) if sv != 0: if (i == (len(s) - 1)) and (sv == 0x80): diff --git a/bitcoin/serialize.py b/bitcoin/serialize.py index 240d8bd..7734dac 100644 --- a/bitcoin/serialize.py +++ b/bitcoin/serialize.py @@ -6,66 +6,74 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from __future__ import absolute_import, division, print_function, unicode_literals + import struct import hashlib +# Py3 compatibility +import sys +bchr = chr +if sys.version > '3': + bchr = lambda x: bytes([x]) + def deser_string(f): - nit = struct.unpack(">= 32 return rs def ser_uint160(u): - rs = "" - for i in xrange(5): - rs += struct.pack(">= 32 return rs def uint160_from_str(s): - r = 0L - t = struct.unpack("> 24) & 0xFF - v = (c & 0xFFFFFFL) << (8 * (nbytes - 3)) + v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) return v def uint256_to_shortstr(u): @@ -73,15 +81,15 @@ def uint256_to_shortstr(u): return s[:16] def deser_vector(f, c, arg1=None): - nit = struct.unpack("