X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=p2pool%2Fbitcoin%2Fdata.py;h=74cb94bb7bc51986c4a9dd6ddda9fb0b862e1aa6;hb=f732111a6e08d7d0649c330d1c703535a8ea80b5;hp=0fa7432ff5589dcf81ac7945dbaf1b2921c0bff2;hpb=16bfca6006f12ebbb1969358695e3db15edca2bc;p=p2pool.git diff --git a/p2pool/bitcoin/data.py b/p2pool/bitcoin/data.py index 0fa7432..74cb94b 100644 --- a/p2pool/bitcoin/data.py +++ b/p2pool/bitcoin/data.py @@ -1,498 +1,325 @@ from __future__ import division -import struct import hashlib +import random import warnings -from . import base58 -from p2pool.util import bases, math +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)) -class Type(object): - # the same data can have only one unpacked representation, but multiple packed binary representations - - #def __hash__(self): - # return hash(tuple(self.__dict__.items())) - - #def __eq__(self, other): - # if not isinstance(other, Type): - # raise NotImplementedError() - # return self.__dict__ == other.__dict__ - - 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 __debug__: - data2 = self._pack(obj) - if data2 != data: - assert self._unpack(data2) == obj - - return obj - - def pack(self, obj): - data = self._pack(obj) - - assert self._unpack(data) == obj - - return data - - - def pack_base58(self, obj): - return base58.base58_encode(self.pack(obj)) - - def unpack_base58(self, base58_data): - return self.unpack(base58.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()) - -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 = pack.IntType(32).unpack(bits2) + return cls(bits) - def write(self, file, item): - return self._inner.write(file, self._target_to_bits(item)) + def __init__(self, bits, target=None): + self.bits = bits + self._target = None + if target is not None and self.target != target: + raise ValueError('target does not match') - def truncate_to(self, x): - return self._bits_to_target(self._target_to_bits(x, _check=False)) + @property + def target(self): + res = self._target + if res is None: + res = self._target = math.shift_left(self.bits & 0x00ffffff, 8 * ((self.bits >> 24) - 3)) + return res - def _bits_to_target(self, bits2): - target = math.shift_left(bits2 & 0x00ffffff, 8 * ((bits2 >> 24) - 3)) - assert target == self._bits_to_target1(struct.pack("= 128: - n = "\x00" + n - bits2 = (chr(len(n)) + (n + 3*chr(0))[:3])[::-1] - bits = struct.unpack("H')), + return self._inner.write(file, item.bits) + +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_link(hashes, index): + # XXX optimize this + + hash_list = [(lambda _h=h: _h, i == index, []) for i, h in enumerate(hashes)] + + while len(hash_list) > 1: + hash_list = [ + ( + 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)], + ) + for (left, left_f, left_l), (right, right_f, right_l) in + zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]]) + ] + + res = [x['hash']() for x in hash_list[0][2]] + + assert hash_list[0][1] + 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 dict(branch=res, index=index) + +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(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) + +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(" share - self.reverse_shares = {} # previous_hash -> set of share_hashes - - self.heads = {} # head hash -> tail_hash - self.tails = {} # tail hash -> set of head hashes - self.heights = {} # share_hash -> height_to, other_share_hash - - def add(self, share): - if share.hash in self.shares: - return # XXX raise exception? - - self.shares[share.hash] = share - self.reverse_shares.setdefault(share.previous_hash, set()).add(share.hash) - - if share.hash in self.tails: - heads = self.tails.pop(share.hash) - else: - heads = set([share.hash]) - - if share.previous_hash in self.heads: - tail = self.heads.pop(share.previous_hash) - else: - tail = share.previous_hash - - self.tails.setdefault(tail, set()).update(heads) - if share.previous_hash in self.tails[tail]: - self.tails[tail].remove(share.previous_hash) - - for head in heads: - self.heads[head] = tail - - def get_height_and_last(self, share_hash): - orig = share_hash - height = 0 - updates = [] - while True: - if share_hash is None or share_hash not in self.shares: - break - updates.append((share_hash, height)) - if share_hash in self.heights: - height_inc, share_hash = self.heights[share_hash] - else: - height_inc, share_hash = 1, self.shares[share_hash].previous_hash - height += height_inc - for update_hash, height_then in updates: - self.heights[update_hash] = height - height_then, share_hash - assert (height, share_hash) == self.get_height_and_last2(orig), ((height, share_hash), self.get_height_and_last2(orig)) - return height, share_hash - - def get_height_and_last2(self, share_hash): - height = 0 - while True: - if share_hash not in self.shares: - break - share_hash = self.shares[share_hash].previous_hash - height += 1 - return height, share_hash - - def get_chain_known(self, start_hash): - ''' - Chain starting with item of hash I{start_hash} of items that this Tracker contains - ''' - item_hash_to_get = start_hash - while True: - if item_hash_to_get not in self.shares: - break - share = self.shares[item_hash_to_get] - assert not isinstance(share, long) - yield share - item_hash_to_get = share.previous_hash - - def get_chain_to_root(self, start_hash, root=None): - ''' - Chain of hashes starting with share_hash of shares to the root (doesn't include root) - Raises an error if one is missing - ''' - share_hash_to_get = start_hash - while share_hash_to_get != root: - share = self.shares[share_hash_to_get] - yield share - share_hash_to_get = share.previous_hash - - def get_best_hash(self): - ''' - Returns hash of item with the most items in its chain - ''' - if not self.heads: - return None - return max(self.heads, key=self.get_height_and_last) - -# network definitions - -class Mainnet(object): - BITCOIN_P2P_PREFIX = 'f9beb4d9'.decode('hex') - BITCOIN_P2P_PORT = 8333 - BITCOIN_ADDRESS_VERSION = 0 - -class Testnet(object): - BITCOIN_P2P_PREFIX = 'fabfb5da'.decode('hex') - BITCOIN_P2P_PORT = 18333 - BITCOIN_ADDRESS_VERSION = 111 + print x['pubkey_hash'] + + if x['version'] != net.ADDRESS_VERSION: + raise ValueError('address not for this net!') + return '\x76\xa9' + ('\x14' + pack.IntType(160).pack(x['pubkey_hash'])) + '\x88\xac' + + +# transactions + +def pubkey_to_script2(pubkey): + assert len(pubkey) <= 75 + return (chr(len(pubkey)) + pubkey) + '\xac' + +def pubkey_hash_to_script2(pubkey_hash): + return '\x76\xa9' + ('\x14' + pack.IntType(160).pack(pubkey_hash)) + '\x88\xac' + +def script2_to_address(script2, net): + try: + pubkey = script2[1:-1] + script2_test = pubkey_to_script2(pubkey) + except: + pass + else: + if script2_test == script2: + return pubkey_to_address(pubkey, net) + + try: + pubkey_hash = pack.IntType(160).unpack(script2[3:-2]) + script2_test2 = pubkey_hash_to_script2(pubkey_hash) + except: + pass + else: + if script2_test2 == script2: + return pubkey_hash_to_address(pubkey_hash, net) + +def script2_to_human(script2, net): + try: + pubkey = script2[1:-1] + script2_test = pubkey_to_script2(pubkey) + except: + pass + else: + if script2_test == script2: + return 'Pubkey. Address: %s' % (pubkey_to_address(pubkey, net),) + + try: + pubkey_hash = pack.IntType(160).unpack(script2[3:-2]) + script2_test2 = pubkey_hash_to_script2(pubkey_hash) + except: + pass + else: + if script2_test2 == script2: + return 'Address. Address: %s' % (pubkey_hash_to_address(pubkey_hash, net),) + + return 'Unknown. Script: %s' % (script2.encode('hex'),)