2 Implementation of Bitcoin's p2p protocol
9 from twisted.internet import protocol, task
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)
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),
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))
56 self.get_tx = deferral.ReplyMatcher(lambda hash: self.send_getdata(requests=[dict(type='tx', hash=hash)]))
58 if hasattr(self.factory, 'resetDelay'):
59 self.factory.resetDelay()
60 if hasattr(self.factory, 'gotConnection'):
61 self.factory.gotConnection(self)
63 self.pinger = task.LoopingCall(self.send_ping)
66 message_inv = pack.ComposedType([
67 ('invs', pack.ListType(pack.ComposedType([
68 ('type', pack.EnumType(pack.IntType(32), {'tx': 1, 'block': 2})),
69 ('hash', pack.IntType(256)),
72 def handle_inv(self, invs):
74 if inv['type'] == 'tx':
75 self.factory.new_tx.happened(inv['hash'])
76 elif inv['type'] == 'block':
77 self.factory.new_block.happened(inv['hash'])
79 print 'Unknown inv type', item
81 message_getdata = pack.ComposedType([
82 ('requests', pack.ListType(pack.ComposedType([
83 ('type', pack.EnumType(pack.IntType(32), {'tx': 1, 'block': 2})),
84 ('hash', pack.IntType(256)),
87 message_getblocks = pack.ComposedType([
88 ('version', pack.IntType(32)),
89 ('have', pack.ListType(pack.IntType(256))),
90 ('last', pack.PossiblyNoneType(0, pack.IntType(256))),
92 message_getheaders = pack.ComposedType([
93 ('version', pack.IntType(32)),
94 ('have', pack.ListType(pack.IntType(256))),
95 ('last', pack.PossiblyNoneType(0, pack.IntType(256))),
97 message_getaddr = pack.ComposedType([])
99 message_addr = pack.ComposedType([
100 ('addrs', pack.ListType(pack.ComposedType([
101 ('timestamp', pack.IntType(32)),
102 ('address', bitcoin_data.address_type),
105 def handle_addr(self, addrs):
109 message_tx = pack.ComposedType([
110 ('tx', bitcoin_data.tx_type),
112 def handle_tx(self, tx):
113 self.get_tx.got_response(bitcoin_data.hash256(bitcoin_data.tx_type.pack(tx)), tx)
115 message_block = pack.ComposedType([
116 ('block', bitcoin_data.block_type),
118 def handle_block(self, block):
119 block_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(block['header']))
120 self.get_block.got_response(block_hash, block)
121 self.get_block_header.got_response(block_hash, block['header'])
123 message_headers = pack.ComposedType([
124 ('headers', pack.ListType(bitcoin_data.block_type)),
126 def handle_headers(self, headers):
127 for header in headers:
128 header = header['header']
129 self.get_block_header.got_response(bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header)), header)
130 self.factory.new_headers.happened([header['header'] for header in headers])
132 message_ping = pack.ComposedType([])
133 def handle_ping(self):
136 message_alert = pack.ComposedType([
137 ('message', pack.VarStrType()),
138 ('signature', pack.VarStrType()),
140 def handle_alert(self, message, signature):
141 pass # print 'ALERT:', (message, signature)
143 def connectionLost(self, reason):
144 if hasattr(self.factory, 'gotConnection'):
145 self.factory.gotConnection(None)
146 if hasattr(self, 'pinger'):
149 print >>sys.stderr, 'Bitcoin connection lost. Reason:', reason.getErrorMessage()
151 class ClientFactory(protocol.ReconnectingClientFactory):
156 def __init__(self, net):
158 self.conn = variable.Variable(None)
160 self.new_block = variable.Event()
161 self.new_tx = variable.Event()
162 self.new_headers = variable.Event()
164 def buildProtocol(self, addr):
165 p = self.protocol(self.net)
169 def gotConnection(self, conn):
172 def getProtocol(self):
173 return self.conn.get_not_none()