from __future__ import division import random import time from twisted.internet import defer, protocol, reactor from twisted.python import log import p2pool 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, variable, dicts class Protocol(bitcoin_p2p.BaseProtocol): version = 1 sub_version = p2pool.__version__ def __init__(self, node): self.node = node self._prefix = self.node.net.PREFIX max_payload_length = 1000000 use_checksum = True other_version = None connected2 = False def connectionMade(self): bitcoin_p2p.BaseProtocol.connectionMade(self) self.addr = self.transport.getPeer().host, self.transport.getPeer().port self.send_version( version=self.version, services=0, addr_to=dict( services=0, address=self.transport.getPeer().host, port=self.transport.getPeer().port, ), addr_from=dict( services=0, address=self.transport.getHost().host, port=self.transport.getHost().port, ), nonce=self.node.nonce, sub_version=self.sub_version, mode=1, best_share_hash=self.node.current_work.value['best_share_hash'], ) reactor.callLater(10, self._connect_timeout) self.timeout_delayed = reactor.callLater(100, self._timeout) def _connect_timeout(self): if not self.connected2 and self.transport.connected: print 'Handshake timed out, disconnecting from %s:%i' % self.addr self.transport.loseConnection() def gotPacket(self): if not self.timeout_delayed.called: self.timeout_delayed.cancel() self.timeout_delayed = reactor.callLater(100, self._timeout) def _timeout(self): if self.transport.connected: print 'Connection timed out, disconnecting from %s:%i' % self.addr self.transport.loseConnection() @defer.inlineCallbacks def _think(self): while self.connected2: self.send_ping() yield deferral.sleep(random.expovariate(1/100)) @defer.inlineCallbacks def _think2(self): while self.connected2: self.send_addrme(port=self.node.port) #print 'sending addrme' yield deferral.sleep(random.expovariate(1/(100*len(self.node.peers) + 1))) message_version = bitcoin_data.ComposedType([ ('version', bitcoin_data.StructType(' 100: count = 100 self.send_addrs(addrs=[ dict( timestamp=self.node.addr_store[host, port][2], address=dict( services=self.node.addr_store[host, port][0], address=host, port=port, ), ) for host, port in random.sample(self.node.addr_store.keys(), min(count, len(self.node.addr_store))) ]) message_getshares = bitcoin_data.ComposedType([ ('hashes', bitcoin_data.ListType(bitcoin_data.HashType())), ('parents', bitcoin_data.VarIntType()), ('stops', bitcoin_data.ListType(bitcoin_data.HashType())), ]) def handle_getshares(self, hashes, parents, stops): self.node.handle_get_shares(hashes, parents, stops, self) message_share1as = bitcoin_data.ComposedType([ ('share1as', bitcoin_data.ListType(p2pool_data.share1a_type)), ]) def handle_share1as(self, share1as): shares = [] for share1a in share1as: # use scrypt for Litecoin if (getattr(self.node.net, 'BITCOIN_POW_SCRYPT', False)): hash_ = bitcoin_data.block_header_type.scrypt(share1a['header']); else: hash_ = bitcoin_data.block_header_type.hash256(share1a['header']) if hash_ <= share1a['header']['target']: print 'Dropping peer %s:%i due to invalid share' % self.addr self.transport.loseConnection() return share = p2pool_data.Share.from_share1a(share1a, self.node.net) share.peer = self # XXX shares.append(share) self.node.handle_shares(shares, self) message_share1bs = bitcoin_data.ComposedType([ ('share1bs', bitcoin_data.ListType(p2pool_data.share1b_type)), ]) def handle_share1bs(self, share1bs): shares = [] for share1b in share1bs: # use scrypt for Litecoin if (getattr(self.node.net, 'BITCOIN_POW_SCRYPT', False)): hash_ = bitcoin_data.block_header_type.scrypt(share1b['header']); else: hash_ = bitcoin_data.block_header_type.hash256(share1b['header']) if not hash_ <= share1b['header']['target']: print 'Dropping peer %s:%i due to invalid share' % self.addr self.transport.loseConnection() return share = p2pool_data.Share.from_share1b(share1b, self.node.net) share.peer = self # XXX shares.append(share) self.node.handle_shares(shares, self) def sendShares(self, shares): share1bs = [] share1as = [] # XXX doesn't need to send full block when it's not urgent # eg. when getting history for share in shares: if share.bitcoin_hash <= share.header['target']: share1bs.append(share.as_share1b()) else: share1as.append(share.as_share1a()) def att(f, **kwargs): try: f(**kwargs) except bitcoin_p2p.TooLong: att(f, **dict((k, v[:len(v)//2]) for k, v in kwargs.iteritems())) att(f, **dict((k, v[len(v)//2:]) for k, v in kwargs.iteritems())) if share1bs: att(self.send_share1bs, share1bs=share1bs) if share1as: att(self.send_share1as, share1as=share1as) def connectionLost(self, reason): if self.connected2: self.node.lost_conn(self) class ServerFactory(protocol.ServerFactory): def __init__(self, node): self.node = node def buildProtocol(self, addr): p = Protocol(self.node) p.factory = self return p class ClientFactory(protocol.ClientFactory): def __init__(self, node): self.node = node def buildProtocol(self, addr): p = Protocol(self.node) p.factory = self return p def startedConnecting(self, connector): self.node.attempt_started(connector) def clientConnectionFailed(self, connector, reason): self.node.attempt_failed(connector) def clientConnectionLost(self, connector, reason): self.node.attempt_ended(connector) addrdb_key = bitcoin_data.ComposedType([ ('address', bitcoin_data.IPV6AddressType()), ('port', bitcoin_data.StructType('>H')), ]) addrdb_value = bitcoin_data.ComposedType([ ('services', bitcoin_data.StructType('