from __future__ import division
-import binascii
import hashlib
-import struct
-from . import base58
-from p2pool.util import bases, math
-import p2pool
+from p2pool.util import bases, math, pack
-class EarlyEnd(Exception):
- pass
-
-class LateEnd(Exception):
- pass
-
-def read((data, pos), length):
- data2 = data[pos:pos + length]
- if len(data2) != length:
- raise EarlyEnd()
- return data2, (data, pos + length)
-
-def size((data, pos)):
- return len(data) - pos
-
-class Type(object):
- __slots__ = []
-
- # 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 pack(self, obj):
- data = self._pack(obj)
-
- if p2pool.DEBUG:
- if self._unpack(data) != obj:
- raise AssertionError((self._unpack(data), obj))
-
- return data
-
-
- 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 IntType(160).unpack(hashlib.new('ripemd160', hashlib.sha256(self.pack(obj)).digest()).digest())
-
- def hash256(self, obj):
- return IntType(256).unpack(hashlib.sha256(hashlib.sha256(self.pack(obj)).digest()).digest())
-
- def scrypt(self, obj):
- import ltc_scrypt
- return IntType(256).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 = '<H', 2
- elif first == 0xfe:
- desc, length = '<I', 4
- elif first == 0xff:
- desc, length = '<Q', 8
- else:
- raise AssertionError()
- data, file = read(file, length)
- return struct.unpack(desc, data)[0], file
-
- def write(self, file, item):
- if item < 0xfd:
- file = file, struct.pack('<B', item)
- elif item <= 0xffff:
- file = file, struct.pack('<BH', 0xfd, item)
- elif item <= 0xffffffff:
- file = file, struct.pack('<BI', 0xfe, item)
- elif item <= 0xffffffffffffffff:
- file = file, struct.pack('<BQ', 0xff, item)
- else:
- raise ValueError('int too large for varint')
- return file
-
-class VarStrType(Type):
- _inner_size = VarIntType()
-
- def read(self, file):
- length, file = self._inner_size.read(file)
- return read(file, length)
-
- def write(self, file, item):
- return self._inner_size.write(file, len(item)), item
-
-class PassthruType(Type):
- def read(self, file):
- return read(file, size(file))
-
- def write(self, file, item):
- return file, item
-
-class EnumType(Type):
- def __init__(self, inner, values):
- self.inner = inner
- self.values = values
-
- keys = {}
- for k, v in values.iteritems():
- if v in keys:
- raise ValueError('duplicate value in values')
- keys[v] = k
- self.keys = keys
-
- def read(self, file):
- data, file = self.inner.read(file)
- if data not in self.keys:
- raise ValueError('enum data (%r) not in values (%r)' % (data, self.values))
- return self.keys[data], file
-
- def write(self, file, item):
- if item not in self.values:
- raise ValueError('enum item (%r) not in values (%r)' % (item, self.values))
- return self.inner.write(file, self.values[item])
-
-class ListType(Type):
- _inner_size = VarIntType()
-
- def __init__(self, type):
- self.type = type
-
- def read(self, file):
- length, file = self._inner_size.read(file)
- res = []
- for i in xrange(length):
- item, file = self.type.read(file)
- res.append(item)
- return res, file
-
- def write(self, file, item):
- file = self._inner_size.write(file, len(item))
- for subitem in item:
- file = self.type.write(file, subitem)
- return file
-
-class StructType(Type):
- __slots__ = 'desc length'.split(' ')
-
- def __init__(self, desc):
- self.desc = desc
- self.length = struct.calcsize(self.desc)
-
- def read(self, file):
- data, file = read(file, self.length)
- return struct.unpack(self.desc, data)[0], file
-
- def write(self, file, item):
- return file, struct.pack(self.desc, item)
-
-class IntType(Type):
- __slots__ = 'bytes step format_str max'.split(' ')
-
- def __new__(cls, bits, endianness='little'):
- assert bits % 8 == 0
- assert endianness in ['little', 'big']
- if bits in [8, 16, 32, 64]:
- return StructType(('<' if endianness == 'little' else '>') + {8: 'B', 16: 'H', 32: 'I', 64: 'Q'}[bits])
- else:
- return Type.__new__(cls, bits, endianness)
-
- def __init__(self, bits, endianness='little'):
- assert bits % 8 == 0
- assert endianness in ['little', 'big']
- self.bytes = bits//8
- self.step = -1 if endianness == 'little' else 1
- self.format_str = '%%0%ix' % (2*self.bytes)
- self.max = 2**bits
-
- def read(self, file, b2a_hex=binascii.b2a_hex):
- data, file = read(file, self.bytes)
- return int(b2a_hex(data[::self.step]), 16), file
-
- def write(self, file, item, a2b_hex=binascii.a2b_hex):
- if not 0 <= item < self.max:
- raise ValueError('invalid int value - %r' % (item,))
- return file, a2b_hex(self.format_str % (item,))[::self.step]
-
-class IPV6AddressType(Type):
- def read(self, file):
- data, file = read(file, 16)
- if data[:12] != '00000000000000000000ffff'.decode('hex'):
- raise ValueError('ipv6 addresses not supported yet')
- return '.'.join(str(ord(x)) for x in data[12:]), file
-
- def write(self, file, item):
- bits = map(int, item.split('.'))
- if len(bits) != 4:
- raise ValueError('invalid address: %r' % (bits,))
- data = '00000000000000000000ffff'.decode('hex') + ''.join(chr(x) for x in bits)
- assert len(data) == 16, len(data)
- return file, data
-
-_record_types = {}
-
-def get_record(fields):
- fields = tuple(sorted(fields))
- if 'keys' in fields:
- raise ValueError()
- if fields not in _record_types:
- class _Record(object):
- __slots__ = fields
- def __repr__(self):
- return repr(dict(self))
- def __getitem__(self, key):
- return getattr(self, key)
- def __setitem__(self, key, value):
- setattr(self, key, value)
- #def __iter__(self):
- # for field in self.__slots__:
- # yield field, getattr(self, field)
- def keys(self):
- return self.__slots__
- def __eq__(self, other):
- if isinstance(other, dict):
- return dict(self) == other
- elif isinstance(other, _Record):
- return all(self[k] == other[k] for k in self.keys())
- raise TypeError()
- def __ne__(self, other):
- return not (self == other)
- _record_types[fields] = _Record
- return _record_types[fields]()
-
-class ComposedType(Type):
- def __init__(self, fields):
- self.fields = tuple(fields)
-
- def read(self, file):
- item = get_record(k for k, v in self.fields)
- for key, type_ in self.fields:
- item[key], file = type_.read(file)
- return item, file
-
- def write(self, file, item):
- for key, type_ in self.fields:
- file = type_.write(file, item[key])
- return file
-
-class ChecksummedType(Type):
+class ChecksummedType(pack.Type):
def __init__(self, inner):
self.inner = inner
obj, file = self.inner.read(file)
data = self.inner.pack(obj)
- checksum, file = read(file, 4)
+ checksum, file = pack.read(file, 4)
if checksum != hashlib.sha256(hashlib.sha256(data).digest()).digest()[:4]:
raise ValueError('invalid checksum')
if n and ord(n[0]) >= 128:
n = '\x00' + n
bits2 = (chr(len(n)) + (n + 3*chr(0))[:3])[::-1]
- bits = struct.unpack('<I', bits2)[0]
+ bits = pack.IntType(32).unpack(bits2)
return cls(bits)
def __init__(self, bits, target=None):
def __repr__(self):
return 'FloatingInteger(bits=%s, target=%s)' % (hex(self.bits), hex(self.target))
-class FloatingIntegerType(Type):
- _inner = IntType(32)
+class FloatingIntegerType(pack.Type):
+ _inner = pack.IntType(32)
def read(self, file):
bits, file = self._inner.read(file)
def write(self, file, item):
return self._inner.write(file, item.bits)
-class PossiblyNoneType(Type):
- def __init__(self, none_value, inner):
- self.none_value = none_value
- self.inner = inner
-
- def read(self, file):
- value, file = self.inner.read(file)
- return None if value == self.none_value else value, file
-
- def write(self, file, item):
- if item == self.none_value:
- raise ValueError('none_value used')
- return self.inner.write(file, self.none_value if item is None else item)
-
-address_type = ComposedType([
- ('services', IntType(64)),
- ('address', IPV6AddressType()),
- ('port', IntType(16, 'big')),
+address_type = pack.ComposedType([
+ ('services', pack.IntType(64)),
+ ('address', pack.IPV6AddressType()),
+ ('port', pack.IntType(16, 'big')),
])
-tx_type = ComposedType([
- ('version', IntType(32)),
- ('tx_ins', ListType(ComposedType([
- ('previous_output', PossiblyNoneType(dict(hash=0, index=2**32 - 1), ComposedType([
- ('hash', IntType(256)),
- ('index', IntType(32)),
+tx_type = pack.ComposedType([
+ ('version', pack.IntType(32)),
+ ('tx_ins', pack.ListType(pack.ComposedType([
+ ('previous_output', pack.PossiblyNoneType(dict(hash=0, index=2**32 - 1), pack.ComposedType([
+ ('hash', pack.IntType(256)),
+ ('index', pack.IntType(32)),
]))),
- ('script', VarStrType()),
- ('sequence', PossiblyNoneType(2**32 - 1, IntType(32))),
+ ('script', pack.VarStrType()),
+ ('sequence', pack.PossiblyNoneType(2**32 - 1, pack.IntType(32))),
]))),
- ('tx_outs', ListType(ComposedType([
- ('value', IntType(64)),
- ('script', VarStrType()),
+ ('tx_outs', pack.ListType(pack.ComposedType([
+ ('value', pack.IntType(64)),
+ ('script', pack.VarStrType()),
]))),
- ('lock_time', IntType(32)),
+ ('lock_time', pack.IntType(32)),
])
-merkle_branch_type = ListType(IntType(256))
+merkle_branch_type = pack.ListType(pack.IntType(256))
-merkle_tx_type = ComposedType([
+merkle_tx_type = pack.ComposedType([
('tx', tx_type),
- ('block_hash', IntType(256)),
+ ('block_hash', pack.IntType(256)),
('merkle_branch', merkle_branch_type),
- ('index', IntType(32)),
+ ('index', pack.IntType(32)),
])
-block_header_type = ComposedType([
- ('version', IntType(32)),
- ('previous_block', PossiblyNoneType(0, IntType(256))),
- ('merkle_root', IntType(256)),
- ('timestamp', IntType(32)),
+block_header_type = pack.ComposedType([
+ ('version', pack.IntType(32)),
+ ('previous_block', pack.PossiblyNoneType(0, pack.IntType(256))),
+ ('merkle_root', pack.IntType(256)),
+ ('timestamp', pack.IntType(32)),
('bits', FloatingIntegerType()),
- ('nonce', IntType(32)),
+ ('nonce', pack.IntType(32)),
])
-block_type = ComposedType([
+block_type = pack.ComposedType([
('header', block_header_type),
- ('txs', ListType(tx_type)),
+ ('txs', pack.ListType(tx_type)),
])
-aux_pow_type = ComposedType([
+aux_pow_type = pack.ComposedType([
('merkle_tx', merkle_tx_type),
('merkle_branch', merkle_branch_type),
- ('index', IntType(32)),
+ ('index', pack.IntType(32)),
('parent_block_header', block_header_type),
])
-merkle_record_type = ComposedType([
- ('left', IntType(256)),
- ('right', IntType(256)),
+merkle_record_type = pack.ComposedType([
+ ('left', pack.IntType(256)),
+ ('right', pack.IntType(256)),
])
def merkle_hash(hashes):
# human addresses
-human_address_type = ChecksummedType(ComposedType([
- ('version', IntType(8)),
- ('pubkey_hash', IntType(160)),
+human_address_type = ChecksummedType(pack.ComposedType([
+ ('version', pack.IntType(8)),
+ ('pubkey_hash', pack.IntType(160)),
]))
-pubkey_type = PassthruType()
+pubkey_type = pack.PassthruType()
def pubkey_hash_to_address(pubkey_hash, net):
return human_address_type.pack_base58(dict(version=net.ADDRESS_VERSION, pubkey_hash=pubkey_hash))
return ('\x41' + pubkey_type.pack(pubkey)) + '\xac'
def pubkey_hash_to_script2(pubkey_hash):
- return '\x76\xa9' + ('\x14' + IntType(160).pack(pubkey_hash)) + '\x88\xac'
+ return '\x76\xa9' + ('\x14' + pack.IntType(160).pack(pubkey_hash)) + '\x88\xac'
def script2_to_address(script2, net):
try:
return pubkey_to_address(pubkey, net)
try:
- pubkey_hash = IntType(160).unpack(script2[3:-2])
+ pubkey_hash = pack.IntType(160).unpack(script2[3:-2])
script2_test2 = pubkey_hash_to_script2(pubkey_hash)
except:
pass
return 'Pubkey. Address: %s' % (pubkey_to_address(pubkey, net),)
try:
- pubkey_hash = IntType(160).unpack(script2[3:-2])
+ pubkey_hash = pack.IntType(160).unpack(script2[3:-2])
script2_test2 = pubkey_hash_to_script2(pubkey_hash)
except:
pass
from . import data as bitcoin_data
from . import sha256
+from p2pool.util import pack
def _swap4(s):
if len(s) % 4:
getwork = {
'data': _swap4(block_data).encode('hex') + '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000',
'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
- 'target': bitcoin_data.IntType(256).pack(self.share_target).encode('hex'),
+ 'target': pack.IntType(256).pack(self.share_target).encode('hex'),
'midstate': _swap4(sha256.process(block_data[:64])).encode('hex'),
}
merkle_root=attrs['merkle_root'],
timestamp=attrs['timestamp'],
bits=attrs['bits'],
- share_target=bitcoin_data.IntType(256).unpack(getwork['target'].decode('hex')),
+ share_target=pack.IntType(256).unpack(getwork['target'].decode('hex')),
)
if _check:
import p2pool
from . import data as bitcoin_data, getwork
-from p2pool.util import variable, datachunker, deferral, forest
+from p2pool.util import variable, datachunker, deferral, forest, pack
class TooLong(Exception):
pass
start_height=0,
)
- message_version = bitcoin_data.ComposedType([
- ('version', bitcoin_data.IntType(32)),
- ('services', bitcoin_data.IntType(64)),
- ('time', bitcoin_data.IntType(64)),
+ message_version = pack.ComposedType([
+ ('version', pack.IntType(32)),
+ ('services', pack.IntType(64)),
+ ('time', pack.IntType(64)),
('addr_to', bitcoin_data.address_type),
('addr_from', bitcoin_data.address_type),
- ('nonce', bitcoin_data.IntType(64)),
- ('sub_version_num', bitcoin_data.VarStrType()),
- ('start_height', bitcoin_data.IntType(32)),
+ ('nonce', pack.IntType(64)),
+ ('sub_version_num', pack.VarStrType()),
+ ('start_height', pack.IntType(32)),
])
def handle_version(self, version, services, time, addr_to, addr_from, nonce, sub_version_num, start_height):
#print 'VERSION', locals()
self.version_after = version
self.send_verack()
- message_verack = bitcoin_data.ComposedType([])
+ message_verack = pack.ComposedType([])
def handle_verack(self):
self.version = self.version_after
if hasattr(self.factory, 'gotConnection'):
self.factory.gotConnection(self)
- message_inv = bitcoin_data.ComposedType([
- ('invs', bitcoin_data.ListType(bitcoin_data.ComposedType([
- ('type', bitcoin_data.EnumType(bitcoin_data.IntType(32), {'tx': 1, 'block': 2})),
- ('hash', bitcoin_data.IntType(256)),
+ message_inv = pack.ComposedType([
+ ('invs', pack.ListType(pack.ComposedType([
+ ('type', pack.EnumType(pack.IntType(32), {'tx': 1, 'block': 2})),
+ ('hash', pack.IntType(256)),
]))),
])
def handle_inv(self, invs):
else:
print 'Unknown inv type', item
- message_getdata = bitcoin_data.ComposedType([
- ('requests', bitcoin_data.ListType(bitcoin_data.ComposedType([
- ('type', bitcoin_data.EnumType(bitcoin_data.IntType(32), {'tx': 1, 'block': 2})),
- ('hash', bitcoin_data.IntType(256)),
+ message_getdata = pack.ComposedType([
+ ('requests', pack.ListType(pack.ComposedType([
+ ('type', pack.EnumType(pack.IntType(32), {'tx': 1, 'block': 2})),
+ ('hash', pack.IntType(256)),
]))),
])
- message_getblocks = bitcoin_data.ComposedType([
- ('version', bitcoin_data.IntType(32)),
- ('have', bitcoin_data.ListType(bitcoin_data.IntType(256))),
- ('last', bitcoin_data.PossiblyNoneType(0, bitcoin_data.IntType(256))),
+ message_getblocks = pack.ComposedType([
+ ('version', pack.IntType(32)),
+ ('have', pack.ListType(pack.IntType(256))),
+ ('last', pack.PossiblyNoneType(0, pack.IntType(256))),
])
- message_getheaders = bitcoin_data.ComposedType([
- ('version', bitcoin_data.IntType(32)),
- ('have', bitcoin_data.ListType(bitcoin_data.IntType(256))),
- ('last', bitcoin_data.PossiblyNoneType(0, bitcoin_data.IntType(256))),
+ message_getheaders = pack.ComposedType([
+ ('version', pack.IntType(32)),
+ ('have', pack.ListType(pack.IntType(256))),
+ ('last', pack.PossiblyNoneType(0, pack.IntType(256))),
])
- message_getaddr = bitcoin_data.ComposedType([])
+ message_getaddr = pack.ComposedType([])
- message_addr = bitcoin_data.ComposedType([
- ('addrs', bitcoin_data.ListType(bitcoin_data.ComposedType([
- ('timestamp', bitcoin_data.IntType(32)),
+ message_addr = pack.ComposedType([
+ ('addrs', pack.ListType(pack.ComposedType([
+ ('timestamp', pack.IntType(32)),
('address', bitcoin_data.address_type),
]))),
])
for addr in addrs:
pass
- message_tx = bitcoin_data.ComposedType([
+ message_tx = pack.ComposedType([
('tx', bitcoin_data.tx_type),
])
def handle_tx(self, tx):
self.get_tx.got_response(bitcoin_data.tx_type.hash256(tx), tx)
- message_block = bitcoin_data.ComposedType([
+ message_block = pack.ComposedType([
('block', bitcoin_data.block_type),
])
def handle_block(self, block):
self.get_block.got_response(block_hash, block)
self.get_block_header.got_response(block_hash, block['header'])
- message_headers = bitcoin_data.ComposedType([
- ('headers', bitcoin_data.ListType(bitcoin_data.block_type)),
+ message_headers = pack.ComposedType([
+ ('headers', pack.ListType(bitcoin_data.block_type)),
])
def handle_headers(self, headers):
for header in headers:
self.get_block_header.got_response(bitcoin_data.block_header_type.hash256(header), header)
self.factory.new_headers.happened([header['header'] for header in headers])
- message_ping = bitcoin_data.ComposedType([])
+ message_ping = pack.ComposedType([])
def handle_ping(self):
pass
- message_alert = bitcoin_data.ComposedType([
- ('message', bitcoin_data.VarStrType()),
- ('signature', bitcoin_data.VarStrType()),
+ message_alert = pack.ComposedType([
+ ('message', pack.VarStrType()),
+ ('signature', pack.VarStrType()),
])
def handle_alert(self, message, signature):
print 'ALERT:', (message, signature)
-from p2pool.bitcoin import data as bitcoin_data
-from p2pool.util import bases
+from p2pool.util import bases, pack
def reads_nothing(f):
return '', f
def protoPUSH(length):
- return lambda f: bitcoin_data.read(f, length)
+ return lambda f: pack.read(f, length)
def protoPUSHDATA(size_len):
def _(f):
- length_str, f = bitcoin_data.read(f, size_len)
+ length_str, f = pack.read(f, size_len)
length = bases.string_to_natural(length_str[::-1].lstrip(chr(0)))
- data, f = bitcoin_data.read(f, length)
+ data, f = pack.read(f, length)
return data, f
return _
def parse(script):
f = script, 0
- while bitcoin_data.size(f):
- opcode_str, f = bitcoin_data.read(f, 1)
+ while pack.size(f):
+ opcode_str, f = pack.read(f, 1)
opcode = ord(opcode_str)
opcode_name, read_func = opcodes[opcode]
opcode_arg, f = read_func(f)
import p2pool
from p2pool import skiplists
from p2pool.bitcoin import data as bitcoin_data, script
-from p2pool.util import math, forest
+from p2pool.util import math, forest, pack
-share_data_type = bitcoin_data.ComposedType([
- ('previous_share_hash', bitcoin_data.PossiblyNoneType(0, bitcoin_data.IntType(256))),
- ('coinbase', bitcoin_data.VarStrType()),
- ('nonce', bitcoin_data.VarStrType()),
- ('new_script', bitcoin_data.VarStrType()),
- ('subsidy', bitcoin_data.IntType(64)),
- ('donation', bitcoin_data.IntType(16)),
- ('stale_info', bitcoin_data.IntType(8)), # 0 nothing, 253 orphan, 254 doa. previously: 254*perfect_round(my_stale_prop), None if no shares
+share_data_type = pack.ComposedType([
+ ('previous_share_hash', pack.PossiblyNoneType(0, pack.IntType(256))),
+ ('coinbase', pack.VarStrType()),
+ ('nonce', pack.VarStrType()),
+ ('new_script', pack.VarStrType()),
+ ('subsidy', pack.IntType(64)),
+ ('donation', pack.IntType(16)),
+ ('stale_info', pack.IntType(8)), # 0 nothing, 253 orphan, 254 doa. previously: 254*perfect_round(my_stale_prop), None if no shares
])
-share_info_type = bitcoin_data.ComposedType([
+share_info_type = pack.ComposedType([
('share_data', share_data_type),
('bits', bitcoin_data.FloatingIntegerType()),
- ('timestamp', bitcoin_data.IntType(32)),
+ ('timestamp', pack.IntType(32)),
])
-share1a_type = bitcoin_data.ComposedType([
+share1a_type = pack.ComposedType([
('header', bitcoin_data.block_header_type),
('share_info', share_info_type),
('merkle_branch', bitcoin_data.merkle_branch_type),
])
-share1b_type = bitcoin_data.ComposedType([
+share1b_type = pack.ComposedType([
('header', bitcoin_data.block_header_type),
('share_info', share_info_type),
- ('other_txs', bitcoin_data.ListType(bitcoin_data.tx_type)),
+ ('other_txs', pack.ListType(bitcoin_data.tx_type)),
])
# type:
# 0: share1a
# 1: share1b
-share_type = bitcoin_data.ComposedType([
- ('type', bitcoin_data.VarIntType()),
- ('contents', bitcoin_data.VarStrType()),
+share_type = pack.ComposedType([
+ ('type', pack.VarIntType()),
+ ('contents', pack.VarStrType()),
])
class Share(object):
sequence=None,
script=share_data['coinbase'].ljust(2, '\x00'),
)],
- tx_outs=[dict(value=0, script='\x20' + bitcoin_data.IntType(256).pack(share_info_type.hash256(share_info)))] + [dict(value=amounts[script], script=script) for script in dests if amounts[script]],
+ tx_outs=[dict(value=0, script='\x20' + pack.IntType(256).pack(share_info_type.hash256(share_info)))] + [dict(value=amounts[script], script=script) for script in dests if amounts[script]],
lock_time=0,
)
import bitcoin.p2p as bitcoin_p2p, bitcoin.getwork as bitcoin_getwork, bitcoin.data as bitcoin_data
from bitcoin import worker_interface
-from util import expiring_dict, jsonrpc, variable, deferral, math, logging
+from util import expiring_dict, jsonrpc, variable, deferral, math, logging, pack
from . import p2p, networks, graphs
import p2pool, p2pool.data as p2pool_data
auxblock = yield deferral.retry('Error while calling merged getauxblock:', 1)(merged_proxy.rpc_getauxblock)()
pre_merged_work.set(dict(
hash=int(auxblock['hash'], 16),
- target=bitcoin_data.IntType(256).unpack(auxblock['target'].decode('hex')),
+ target=pack.IntType(256).unpack(auxblock['target'].decode('hex')),
chain_id=auxblock['chainid'],
))
yield deferral.sleep(1)
share_data=dict(
previous_share_hash=current_work.value['best_share_hash'],
coinbase=(('' if current_work.value['aux_work'] is None else
- '\xfa\xbemm' + bitcoin_data.IntType(256, 'big').pack(current_work.value['aux_work']['hash']) + struct.pack('<ii', 1, 0)) + current_work.value['coinbaseflags'])[:100],
+ '\xfa\xbemm' + pack.IntType(256, 'big').pack(current_work.value['aux_work']['hash']) + struct.pack('<ii', 1, 0)) + current_work.value['coinbaseflags'])[:100],
nonce=struct.pack('<Q', random.randrange(2**64)),
new_script=payout_script,
subsidy=current_work2.value['subsidy'],
try:
if aux_work is not None and (pow_hash <= aux_work['target'] or p2pool.DEBUG):
- assert bitcoin_data.IntType(256, 'big').pack(aux_work['hash']).encode('hex') == transactions[0]['tx_ins'][0]['script'][4:4+32].encode('hex')
+ assert pack.IntType(256, 'big').pack(aux_work['hash']).encode('hex') == transactions[0]['tx_ins'][0]['script'][4:4+32].encode('hex')
df = deferral.retry('Error submitting merged block: (will retry)', 10, 10)(merged_proxy.rpc_getauxblock)(
- bitcoin_data.IntType(256, 'big').pack(aux_work['hash']).encode('hex'),
+ pack.IntType(256, 'big').pack(aux_work['hash']).encode('hex'),
bitcoin_data.aux_pow_type.pack(dict(
merkle_tx=dict(
tx=transactions[0],
def _work_changed(self, new_work):
share = tracker.shares[new_work['best_share_hash']]
if share.pow_hash <= share.header['bits'].target and share.header_hash not in self.announced_hashes:
- self.say('#p2pool', '\x033,4BLOCK FOUND! http://blockexplorer.com/block/' + bitcoin_data.IntType(256, 'big').pack(share.header_hash).encode('hex'))
+ self.say('#p2pool', '\x033,4BLOCK FOUND! http://blockexplorer.com/block/' + pack.IntType(256, 'big').pack(share.header_hash).encode('hex'))
def connectionLost(self, reason):
current_work.changed.unwatch(self.watch_id)
class IRCClientFactory(protocol.ReconnectingClientFactory):
from p2pool import data as p2pool_data
from p2pool.bitcoin import p2p as bitcoin_p2p
from p2pool.bitcoin import data as bitcoin_data
-from p2pool.util import deferral
+from p2pool.util import deferral, pack
class Protocol(bitcoin_p2p.BaseProtocol):
version = 2
#print 'sending addrme'
yield deferral.sleep(random.expovariate(1/(100*len(self.node.peers) + 1)))
- message_version = bitcoin_data.ComposedType([
- ('version', bitcoin_data.IntType(32)),
- ('services', bitcoin_data.IntType(64)),
+ message_version = pack.ComposedType([
+ ('version', pack.IntType(32)),
+ ('services', pack.IntType(64)),
('addr_to', bitcoin_data.address_type),
('addr_from', bitcoin_data.address_type),
- ('nonce', bitcoin_data.IntType(64)),
- ('sub_version', bitcoin_data.VarStrType()),
- ('mode', bitcoin_data.IntType(32)), # always 1 for legacy compatibility
- ('best_share_hash', bitcoin_data.PossiblyNoneType(0, bitcoin_data.IntType(256))),
+ ('nonce', pack.IntType(64)),
+ ('sub_version', pack.VarStrType()),
+ ('mode', pack.IntType(32)), # always 1 for legacy compatibility
+ ('best_share_hash', pack.PossiblyNoneType(0, pack.IntType(256))),
])
def handle_version(self, version, services, addr_to, addr_from, nonce, sub_version, mode, best_share_hash):
if self.other_version is not None or version < 2:
if best_share_hash is not None:
self.node.handle_share_hashes([best_share_hash], self)
- message_ping = bitcoin_data.ComposedType([])
+ message_ping = pack.ComposedType([])
def handle_ping(self):
pass
- message_addrme = bitcoin_data.ComposedType([
- ('port', bitcoin_data.IntType(16)),
+ message_addrme = pack.ComposedType([
+ ('port', pack.IntType(16)),
])
def handle_addrme(self, port):
host = self.transport.getPeer().host
),
])
- message_addrs = bitcoin_data.ComposedType([
- ('addrs', bitcoin_data.ListType(bitcoin_data.ComposedType([
- ('timestamp', bitcoin_data.IntType(64)),
+ message_addrs = pack.ComposedType([
+ ('addrs', pack.ListType(pack.ComposedType([
+ ('timestamp', pack.IntType(64)),
('address', bitcoin_data.address_type),
]))),
])
if random.random() < .8 and self.node.peers:
random.choice(self.node.peers.values()).send_addrs(addrs=[addr_record])
- message_getaddrs = bitcoin_data.ComposedType([
- ('count', bitcoin_data.IntType(32)),
+ message_getaddrs = pack.ComposedType([
+ ('count', pack.IntType(32)),
])
def handle_getaddrs(self, count):
if count > 100:
self.node.get_good_peers(count)
])
- message_getshares = bitcoin_data.ComposedType([
- ('hashes', bitcoin_data.ListType(bitcoin_data.IntType(256))),
- ('parents', bitcoin_data.VarIntType()),
- ('stops', bitcoin_data.ListType(bitcoin_data.IntType(256))),
+ message_getshares = pack.ComposedType([
+ ('hashes', pack.ListType(pack.IntType(256))),
+ ('parents', pack.VarIntType()),
+ ('stops', pack.ListType(pack.IntType(256))),
])
def handle_getshares(self, hashes, parents, stops):
self.node.handle_get_shares(hashes, parents, stops, self)
- message_shares = bitcoin_data.ComposedType([
- ('shares', bitcoin_data.ListType(p2pool_data.share_type)),
+ message_shares = pack.ComposedType([
+ ('shares', pack.ListType(p2pool_data.share_type)),
])
def handle_shares(self, shares):
res = []
import unittest
from p2pool.bitcoin import data, networks
+from p2pool.util import pack
class Test(unittest.TestCase):
)],
tx_outs=[dict(
value=5003880250,
- script=data.pubkey_hash_to_script2(data.IntType(160).unpack('ca975b00a8c203b8692f5a18d92dc5c2d2ebc57b'.decode('hex'))),
+ script=data.pubkey_hash_to_script2(pack.IntType(160).unpack('ca975b00a8c203b8692f5a18d92dc5c2d2ebc57b'.decode('hex'))),
)],
lock_time=0,
)) == 0xb53802b2333e828d6532059f46ecf6b313a42d79f97925e457fbbfda45367e5c
def test_address_to_pubkey_hash(self):
- assert data.address_to_pubkey_hash('1KUCp7YP5FP8ViRxhfszSUJCTAajK6viGy', networks.BitcoinMainnet) == data.IntType(160).unpack('ca975b00a8c203b8692f5a18d92dc5c2d2ebc57b'.decode('hex'))
+ assert data.address_to_pubkey_hash('1KUCp7YP5FP8ViRxhfszSUJCTAajK6viGy', networks.BitcoinMainnet) == pack.IntType(160).unpack('ca975b00a8c203b8692f5a18d92dc5c2d2ebc57b'.decode('hex'))
def test_merkle_hash(self):
assert data.merkle_hash([
--- /dev/null
+import binascii
+import hashlib
+import struct
+
+from p2pool.bitcoin import base58
+import p2pool
+
+class EarlyEnd(Exception):
+ pass
+
+class LateEnd(Exception):
+ pass
+
+def read((data, pos), length):
+ data2 = data[pos:pos + length]
+ if len(data2) != length:
+ raise EarlyEnd()
+ return data2, (data, pos + length)
+
+def size((data, pos)):
+ return len(data) - pos
+
+class Type(object):
+ __slots__ = []
+
+ # 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 pack(self, obj):
+ data = self._pack(obj)
+
+ if p2pool.DEBUG:
+ if self._unpack(data) != obj:
+ raise AssertionError((self._unpack(data), obj))
+
+ return data
+
+
+ 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 IntType(160).unpack(hashlib.new('ripemd160', hashlib.sha256(self.pack(obj)).digest()).digest())
+
+ def hash256(self, obj):
+ return IntType(256).unpack(hashlib.sha256(hashlib.sha256(self.pack(obj)).digest()).digest())
+
+ def scrypt(self, obj):
+ import ltc_scrypt
+ return IntType(256).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 = '<H', 2
+ elif first == 0xfe:
+ desc, length = '<I', 4
+ elif first == 0xff:
+ desc, length = '<Q', 8
+ else:
+ raise AssertionError()
+ data, file = read(file, length)
+ return struct.unpack(desc, data)[0], file
+
+ def write(self, file, item):
+ if item < 0xfd:
+ file = file, struct.pack('<B', item)
+ elif item <= 0xffff:
+ file = file, struct.pack('<BH', 0xfd, item)
+ elif item <= 0xffffffff:
+ file = file, struct.pack('<BI', 0xfe, item)
+ elif item <= 0xffffffffffffffff:
+ file = file, struct.pack('<BQ', 0xff, item)
+ else:
+ raise ValueError('int too large for varint')
+ return file
+
+class VarStrType(Type):
+ _inner_size = VarIntType()
+
+ def read(self, file):
+ length, file = self._inner_size.read(file)
+ return read(file, length)
+
+ def write(self, file, item):
+ return self._inner_size.write(file, len(item)), item
+
+class PassthruType(Type):
+ def read(self, file):
+ return read(file, size(file))
+
+ def write(self, file, item):
+ return file, item
+
+class EnumType(Type):
+ def __init__(self, inner, values):
+ self.inner = inner
+ self.values = values
+
+ keys = {}
+ for k, v in values.iteritems():
+ if v in keys:
+ raise ValueError('duplicate value in values')
+ keys[v] = k
+ self.keys = keys
+
+ def read(self, file):
+ data, file = self.inner.read(file)
+ if data not in self.keys:
+ raise ValueError('enum data (%r) not in values (%r)' % (data, self.values))
+ return self.keys[data], file
+
+ def write(self, file, item):
+ if item not in self.values:
+ raise ValueError('enum item (%r) not in values (%r)' % (item, self.values))
+ return self.inner.write(file, self.values[item])
+
+class ListType(Type):
+ _inner_size = VarIntType()
+
+ def __init__(self, type):
+ self.type = type
+
+ def read(self, file):
+ length, file = self._inner_size.read(file)
+ res = []
+ for i in xrange(length):
+ item, file = self.type.read(file)
+ res.append(item)
+ return res, file
+
+ def write(self, file, item):
+ file = self._inner_size.write(file, len(item))
+ for subitem in item:
+ file = self.type.write(file, subitem)
+ return file
+
+class StructType(Type):
+ __slots__ = 'desc length'.split(' ')
+
+ def __init__(self, desc):
+ self.desc = desc
+ self.length = struct.calcsize(self.desc)
+
+ def read(self, file):
+ data, file = read(file, self.length)
+ return struct.unpack(self.desc, data)[0], file
+
+ def write(self, file, item):
+ return file, struct.pack(self.desc, item)
+
+class IntType(Type):
+ __slots__ = 'bytes step format_str max'.split(' ')
+
+ def __new__(cls, bits, endianness='little'):
+ assert bits % 8 == 0
+ assert endianness in ['little', 'big']
+ if bits in [8, 16, 32, 64]:
+ return StructType(('<' if endianness == 'little' else '>') + {8: 'B', 16: 'H', 32: 'I', 64: 'Q'}[bits])
+ else:
+ return Type.__new__(cls, bits, endianness)
+
+ def __init__(self, bits, endianness='little'):
+ assert bits % 8 == 0
+ assert endianness in ['little', 'big']
+ self.bytes = bits//8
+ self.step = -1 if endianness == 'little' else 1
+ self.format_str = '%%0%ix' % (2*self.bytes)
+ self.max = 2**bits
+
+ def read(self, file, b2a_hex=binascii.b2a_hex):
+ data, file = read(file, self.bytes)
+ return int(b2a_hex(data[::self.step]), 16), file
+
+ def write(self, file, item, a2b_hex=binascii.a2b_hex):
+ if not 0 <= item < self.max:
+ raise ValueError('invalid int value - %r' % (item,))
+ return file, a2b_hex(self.format_str % (item,))[::self.step]
+
+class IPV6AddressType(Type):
+ def read(self, file):
+ data, file = read(file, 16)
+ if data[:12] != '00000000000000000000ffff'.decode('hex'):
+ raise ValueError('ipv6 addresses not supported yet')
+ return '.'.join(str(ord(x)) for x in data[12:]), file
+
+ def write(self, file, item):
+ bits = map(int, item.split('.'))
+ if len(bits) != 4:
+ raise ValueError('invalid address: %r' % (bits,))
+ data = '00000000000000000000ffff'.decode('hex') + ''.join(chr(x) for x in bits)
+ assert len(data) == 16, len(data)
+ return file, data
+
+_record_types = {}
+
+def get_record(fields):
+ fields = tuple(sorted(fields))
+ if 'keys' in fields:
+ raise ValueError()
+ if fields not in _record_types:
+ class _Record(object):
+ __slots__ = fields
+ def __repr__(self):
+ return repr(dict(self))
+ def __getitem__(self, key):
+ return getattr(self, key)
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
+ #def __iter__(self):
+ # for field in self.__slots__:
+ # yield field, getattr(self, field)
+ def keys(self):
+ return self.__slots__
+ def __eq__(self, other):
+ if isinstance(other, dict):
+ return dict(self) == other
+ elif isinstance(other, _Record):
+ return all(self[k] == other[k] for k in self.keys())
+ raise TypeError()
+ def __ne__(self, other):
+ return not (self == other)
+ _record_types[fields] = _Record
+ return _record_types[fields]()
+
+class ComposedType(Type):
+ def __init__(self, fields):
+ self.fields = tuple(fields)
+
+ def read(self, file):
+ item = get_record(k for k, v in self.fields)
+ for key, type_ in self.fields:
+ item[key], file = type_.read(file)
+ return item, file
+
+ def write(self, file, item):
+ for key, type_ in self.fields:
+ file = type_.write(file, item[key])
+ return file
+
+class PossiblyNoneType(Type):
+ def __init__(self, none_value, inner):
+ self.none_value = none_value
+ self.inner = inner
+
+ def read(self, file):
+ value, file = self.inner.read(file)
+ return None if value == self.none_value else value, file
+
+ def write(self, file, item):
+ if item == self.none_value:
+ raise ValueError('none_value used')
+ return self.inner.write(file, self.none_value if item is None else item)