2 Implementation of Bitcoin's p2p protocol
9 from twisted.internet import protocol
12 from . import data as bitcoin_data
13 from p2pool.util import deferral, p2protocol, pack, variable
15 class Protocol(p2protocol.Protocol):
16 def __init__(self, net):
17 p2protocol.Protocol.__init__(self, net.P2P_PREFIX, 1000000, ignore_trailing_payload=True)
19 def connectionMade(self):
23 time=int(time.time()),
26 address=self.transport.getPeer().host,
27 port=self.transport.getPeer().port,
31 address=self.transport.getHost().host,
32 port=self.transport.getHost().port,
34 nonce=random.randrange(2**64),
35 sub_version_num='/P2Pool:%s/' % (p2pool.__version__,),
39 message_version = pack.ComposedType([
40 ('version', pack.IntType(32)),
41 ('services', pack.IntType(64)),
42 ('time', pack.IntType(64)),
43 ('addr_to', bitcoin_data.address_type),
44 ('addr_from', bitcoin_data.address_type),
45 ('nonce', pack.IntType(64)),
46 ('sub_version_num', pack.VarStrType()),
47 ('start_height', pack.IntType(32)),
49 def handle_version(self, version, services, time, addr_to, addr_from, nonce, sub_version_num, start_height):
52 message_verack = pack.ComposedType([])
53 def handle_verack(self):
54 self.get_block = deferral.ReplyMatcher(lambda hash: self.send_getdata(requests=[dict(type='block', hash=hash)]))
55 self.get_block_header = deferral.ReplyMatcher(lambda hash: self.send_getheaders(version=1, have=[], last=hash))
57 if hasattr(self.factory, 'resetDelay'):
58 self.factory.resetDelay()
59 if hasattr(self.factory, 'gotConnection'):
60 self.factory.gotConnection(self)
62 self.pinger = deferral.RobustLoopingCall(self.send_ping, nonce=1234)
65 message_inv = pack.ComposedType([
66 ('invs', pack.ListType(pack.ComposedType([
67 ('type', pack.EnumType(pack.IntType(32), {1: 'tx', 2: 'block'})),
68 ('hash', pack.IntType(256)),
71 def handle_inv(self, invs):
73 if inv['type'] == 'tx':
74 self.send_getdata(requests=[inv])
75 elif inv['type'] == 'block':
76 self.factory.new_block.happened(inv['hash'])
78 print 'Unknown inv type', inv
80 message_getdata = pack.ComposedType([
81 ('requests', pack.ListType(pack.ComposedType([
82 ('type', pack.EnumType(pack.IntType(32), {1: 'tx', 2: 'block'})),
83 ('hash', pack.IntType(256)),
86 message_getblocks = pack.ComposedType([
87 ('version', pack.IntType(32)),
88 ('have', pack.ListType(pack.IntType(256))),
89 ('last', pack.PossiblyNoneType(0, pack.IntType(256))),
91 message_getheaders = pack.ComposedType([
92 ('version', pack.IntType(32)),
93 ('have', pack.ListType(pack.IntType(256))),
94 ('last', pack.PossiblyNoneType(0, pack.IntType(256))),
96 message_getaddr = pack.ComposedType([])
98 message_addr = pack.ComposedType([
99 ('addrs', pack.ListType(pack.ComposedType([
100 ('timestamp', pack.IntType(32)),
101 ('address', bitcoin_data.address_type),
104 def handle_addr(self, addrs):
108 message_tx = pack.ComposedType([
109 ('tx', bitcoin_data.tx_type),
111 def handle_tx(self, tx):
112 self.factory.new_tx.happened(tx)
114 message_block = pack.ComposedType([
115 ('block', bitcoin_data.block_type),
117 def handle_block(self, block):
118 block_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(block['header']))
119 self.get_block.got_response(block_hash, block)
120 self.get_block_header.got_response(block_hash, block['header'])
122 message_headers = pack.ComposedType([
123 ('headers', pack.ListType(bitcoin_data.block_type)),
125 def handle_headers(self, headers):
126 for header in headers:
127 header = header['header']
128 self.get_block_header.got_response(bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header)), header)
129 self.factory.new_headers.happened([header['header'] for header in headers])
131 message_ping = pack.ComposedType([
132 ('nonce', pack.IntType(64)),
134 def handle_ping(self, nonce):
135 self.send_pong(nonce=nonce)
137 message_pong = pack.ComposedType([
138 ('nonce', pack.IntType(64)),
140 def handle_pong(self, nonce):
143 message_alert = pack.ComposedType([
144 ('message', pack.VarStrType()),
145 ('signature', pack.VarStrType()),
147 def handle_alert(self, message, signature):
148 pass # print 'ALERT:', (message, signature)
150 def connectionLost(self, reason):
151 if hasattr(self.factory, 'gotConnection'):
152 self.factory.gotConnection(None)
153 if hasattr(self, 'pinger'):
156 print >>sys.stderr, 'Bitcoin connection lost. Reason:', reason.getErrorMessage()
158 class ClientFactory(protocol.ReconnectingClientFactory):
163 def __init__(self, net):
165 self.conn = variable.Variable(None)
167 self.new_block = variable.Event()
168 self.new_tx = variable.Event()
169 self.new_headers = variable.Event()
171 def buildProtocol(self, addr):
172 p = self.protocol(self.net)
176 def gotConnection(self, conn):
179 def getProtocol(self):
180 return self.conn.get_not_none()