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, 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_shares = bitcoin_data.ComposedType([ ('shares', bitcoin_data.ListType(p2pool_data.share_type)), ]) def handle_shares(self, shares): res = [] for share in shares: share_obj = p2pool_data.Share.from_share(share, self.node.net) share_obj.peer = self res.append(share_obj) self.node.handle_shares(res) def sendShares(self, shares, full=False): shares = [] # XXX doesn't need to send full block when it's not urgent # eg. when getting history for share in shares: shares.append(share.as_share()) 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 shares: att(self.send_shares, shares=shares) 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('