X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=p2pool%2Fbitcoin%2Fdata.py;h=74cb94bb7bc51986c4a9dd6ddda9fb0b862e1aa6;hb=f732111a6e08d7d0649c330d1c703535a8ea80b5;hp=57f3d0e4645459b848709465dccf64c02e767f96;hpb=d8930943c0ba45e136b3a55f859e91edc32fe2fb;p=p2pool.git diff --git a/p2pool/bitcoin/data.py b/p2pool/bitcoin/data.py index 57f3d0e..74cb94b 100644 --- a/p2pool/bitcoin/data.py +++ b/p2pool/bitcoin/data.py @@ -1,353 +1,51 @@ from __future__ import division import hashlib -import struct +import random +import warnings -from . import base58 -from p2pool.util import bases, math, expiring_dict, memoize, slush import p2pool +from p2pool.util import math, pack -class EarlyEnd(Exception): - pass +def hash256(data): + return pack.IntType(256).unpack(hashlib.sha256(hashlib.sha256(data).digest()).digest()) -class LateEnd(Exception): - pass +def hash160(data): + return pack.IntType(160).unpack(hashlib.new('ripemd160', hashlib.sha256(data).digest()).digest()) -def read((data, pos), length): - data2 = data[pos:pos + length] - if len(data2) != length: - raise EarlyEnd() - return data2, (data, pos + length) +def scrypt(data): + return pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)) -def size((data, pos)): - return len(data) - pos - -class Type(object): - # the same data can have only one unpacked representation, but multiple packed binary representations - - def __hash__(self): - rval = getattr(self, '_hash', None) - if rval is None: - try: - rval = self._hash = hash((type(self), frozenset(self.__dict__.items()))) - except: - print self.__dict__ - raise - return rval - - def __eq__(self, other): - return type(other) is type(self) and other.__dict__ == self.__dict__ - - def __ne__(self, other): - return not (self == other) - - def _unpack(self, data): - obj, (data2, pos) = self.read((data, 0)) - - assert data2 is data - - if pos != len(data): - raise LateEnd() - - return obj - - def _pack(self, obj): - f = self.write(None, obj) - - res = [] - while f is not None: - res.append(f[1]) - f = f[0] - res.reverse() - return ''.join(res) - - - def unpack(self, data): - obj = self._unpack(data) - - if p2pool.DEBUG: - data2 = self._pack(obj) - if data2 != data: - if self._unpack(data2) != obj: - raise AssertionError() - - return obj - - def pack2(self, obj): - data = self._pack(obj) - - if p2pool.DEBUG: - if self._unpack(data) != obj: - raise AssertionError((self._unpack(data), obj)) - - return data - - _backing = None - - @classmethod - def enable_caching(cls): - assert cls._backing is None - cls._backing = expiring_dict.ExpiringDict(100) - cls._pre_pack2 = cls.pack2 - cls.pack2 = memoize.memoize_with_backing(cls._backing, [cls.unpack])(cls.pack2) - cls._pre_unpack = cls.unpack - cls.unpack = memoize.memoize_with_backing(cls._backing)(cls.unpack) # doesn't have an inverse - - @classmethod - def disable_caching(cls): - assert cls._backing is not None - cls._backing.stop() - cls._backing = None - cls.pack2 = cls._pre_pack2 - del cls._pre_pack2 - cls.unpack = cls._pre_unpack - del cls._pre_unpack - - def pack(self, obj): - return self.pack2(slush.immutify(obj)) - - - def pack_base58(self, obj): - return base58.encode(self.pack(obj)) - - def unpack_base58(self, base58_data): - return self.unpack(base58.decode(base58_data)) - - - def hash160(self, obj): - return ShortHashType().unpack(hashlib.new('ripemd160', hashlib.sha256(self.pack(obj)).digest()).digest()) - - def hash256(self, obj): - return HashType().unpack(hashlib.sha256(hashlib.sha256(self.pack(obj)).digest()).digest()) - - def scrypt(self, obj): - import ltc_scrypt - return HashType().unpack(ltc_scrypt.getPoWHash(self.pack(obj))) - -class VarIntType(Type): - # redundancy doesn't matter here because bitcoin and p2pool both reencode before hashing - def read(self, file): - data, file = read(file, 1) - first = ord(data) - if first < 0xfd: - return first, file - elif first == 0xfd: - desc, length = '= 128: n = '\x00' + n bits2 = (chr(len(n)) + (n + 3*chr(0))[:3])[::-1] - bits = struct.unpack('H')), +address_type = pack.ComposedType([ + ('services', pack.IntType(64)), + ('address', pack.IPV6AddressType()), + ('port', pack.IntType(16, 'big')), ]) -tx_type = ComposedType([ - ('version', StructType(' 1: - hash_list = [merkle_record_type.hash256(dict(left=left, right=left if right is None else right)) - for left, right in zip(hash_list[::2], hash_list[1::2] + [None])] + hash_list = [hash256(merkle_record_type.pack(dict(left=left, right=right))) + for left, right in zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]])] return hash_list[0] -def calculate_merkle_branch(hashes, index): +def calculate_merkle_link(hashes, index): # XXX optimize this - hash_list = [(h, i == index, []) for i, h in enumerate(hashes)] + hash_list = [(lambda _h=h: _h, i == index, []) for i, h in enumerate(hashes)] while len(hash_list) > 1: hash_list = [ ( - merkle_record_type.hash256(dict(left=left, right=right)), + lambda _left=left, _right=right: hash256(merkle_record_type.pack(dict(left=_left(), right=_right()))), left_f or right_f, (left_l if left_f else right_l) + [dict(side=1, hash=right) if left_f else dict(side=0, hash=left)], ) @@ -486,59 +196,112 @@ def calculate_merkle_branch(hashes, index): zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]]) ] - res = [x['hash'] for x in hash_list[0][2]] + res = [x['hash']() for x in hash_list[0][2]] assert hash_list[0][1] - assert check_merkle_branch(hashes[index], index, res) == hash_list[0][0] + if p2pool.DEBUG: + new_hashes = [random.randrange(2**256) if x is None else x + for x in hashes] + assert check_merkle_link(new_hashes[index], dict(branch=res, index=index)) == merkle_hash(new_hashes) assert index == sum(k*2**i for i, k in enumerate([1-x['side'] for x in hash_list[0][2]])) - return res + return dict(branch=res, index=index) -def check_merkle_branch(tip_hash, index, merkle_branch): - return reduce(lambda c, (i, h): merkle_record_type.hash256( - dict(left=h, right=c) if 2**i & index else +def check_merkle_link(tip_hash, link): + if link['index'] >= 2**len(link['branch']): + raise ValueError('index too large') + return reduce(lambda c, (i, h): hash256(merkle_record_type.pack( + dict(left=h, right=c) if (link['index'] >> i) & 1 else dict(left=c, right=h) - ), enumerate(merkle_branch), tip_hash) + )), enumerate(link['branch']), tip_hash) + +# targets def target_to_average_attempts(target): + assert 0 <= target and isinstance(target, (int, long)), target + if target >= 2**256: warnings.warn('target >= 2**256!') return 2**256//(target + 1) +def average_attempts_to_target(average_attempts): + assert average_attempts > 0 + return min(int(2**256/average_attempts - 1 + 0.5), 2**256-1) + def target_to_difficulty(target): + assert 0 <= target and isinstance(target, (int, long)), target + if target >= 2**256: warnings.warn('target >= 2**256!') return (0xffff0000 * 2**(256-64) + 1)/(target + 1) -# tx - -def tx_get_sigop_count(tx): - return sum(script.get_sigop_count(txin['script']) for txin in tx['tx_ins']) + sum(script.get_sigop_count(txout['script']) for txout in tx['tx_outs']) +def difficulty_to_target(difficulty): + assert difficulty >= 0 + if difficulty == 0: return 2**256-1 + return min(int((0xffff0000 * 2**(256-64) + 1)/difficulty - 1 + 0.5), 2**256-1) # human addresses -human_address_type = ChecksummedType(ComposedType([ - ('version', StructType('