2596dc95d5d10f32021dbe005a6f3b7e1f620bc9
[p2pool.git] / p2pool / test / test_node.py
1 from __future__ import division
2
3 import random
4
5 from twisted.internet import defer, reactor
6 from twisted.trial import unittest
7 from twisted.web import resource, server
8
9 from p2pool import data, node, work
10 from p2pool.bitcoin import data as bitcoin_data, networks, worker_interface
11 from p2pool.util import deferral, jsonrpc, math, variable
12
13 @apply
14 class bitcoinp2p(object):
15     def send_block(self, block):
16         pass
17     
18     def get_block_header(self, hash):
19         if hash == 0x16c169477c25421250ec5d32cf9c6d38538b5de970a2355fd89:
20             return defer.succeed({
21                 'nonce': 1853158954,
22                 'timestamp': 1351658517,
23                 'merkle_root': 2282849479936278423916707524932131168473430114569971665822757638339486597658L,
24                 'version': 1,
25                 'previous_block': 1048610514577342396345362905164852351970507722694242579238530L,
26                 'bits': bitcoin_data.FloatingInteger(bits=0x1a0513c5, target=0x513c50000000000000000000000000000000000000000000000L),
27             })
28         print hex(hash)
29         return defer.fail('blah')
30
31 class factory(object):
32     new_headers = variable.Event()
33     new_block = variable.Event()
34     new_tx = variable.Event()
35     conn = variable.Variable(bitcoinp2p)
36     @classmethod
37     def getProtocol(self):
38         return bitcoinp2p
39
40 class bitcoind(object):
41     @classmethod
42     def rpc_help(self):
43         return '\ngetblock '
44     
45     @classmethod
46     def rpc_getblock(self, block_hash_hex):
47         return dict(height=42)
48     
49     @classmethod
50     def rpc_getmemorypool(self, result=None):
51         if result is not None:
52             return True
53         return {
54             "version" : 2,
55             "previousblockhash" : "000000000000016c169477c25421250ec5d32cf9c6d38538b5de970a2355fd89",
56             "transactions" : [
57             ],
58             "coinbaseaux" : {
59                 "flags" : "062f503253482f"
60             },
61             "coinbasevalue" : 5044450000,
62             "target" : "0000000000000513c50000000000000000000000000000000000000000000000",
63             "mintime" : 1351655621,
64             "mutable" : [
65                 "time",
66                 "transactions",
67                 "prevblock"
68             ],
69             "noncerange" : "00000000ffffffff",
70             "sigoplimit" : 20000,
71             "sizelimit" : 1000000,
72             "curtime" : 1351659940,
73             "bits" : "21008000",
74             "height" : 205801
75         }
76
77 mynet = math.Object(
78     PARENT=networks.nets['litecoin_testnet'],
79     SHARE_PERIOD=3, # seconds
80     CHAIN_LENGTH=20*60//3, # shares
81     REAL_CHAIN_LENGTH=20*60//3, # shares
82     TARGET_LOOKBEHIND=200, # shares
83     SPREAD=12, # blocks
84     IDENTIFIER='cca5e24ec6408b1e'.decode('hex'),
85     PREFIX='ad9614f6466a39cf'.decode('hex'),
86     P2P_PORT=19338,
87     MIN_TARGET=2**256 - 1,
88     MAX_TARGET=2**256 - 1,
89     PERSIST=False,
90     WORKER_PORT=19327,
91     BOOTSTRAP_ADDRS='72.14.191.28'.split(' '),
92     ANNOUNCE_CHANNEL='#p2pool-alt',
93     VERSION_CHECK=lambda v: True,
94 )
95
96 class MiniNode(object):
97     @classmethod
98     @defer.inlineCallbacks
99     def start(cls, net, factory, bitcoind, peer_ports):
100         self = cls()
101         
102         self.n = node.Node(factory, bitcoind, [], [], net)
103         yield self.n.start()
104         
105         self.n.p2p_node = node.P2PNode(self.n, 0, 1000000, {}, [('127.0.0.1', peer_port) for peer_port in peer_ports])
106         self.n.p2p_node.start()
107         
108         wb = work.WorkerBridge(node=self.n, my_pubkey_hash=random.randrange(2**160), donation_percentage=random.uniform(0, 10), merged_urls=[], worker_fee=3)
109         web_root = resource.Resource()
110         worker_interface.WorkerInterface(wb).attach_to(web_root)
111         self.web_port = reactor.listenTCP(0, server.Site(web_root))
112         
113         defer.returnValue(self)
114     
115     @defer.inlineCallbacks
116     def stop(self):
117         yield self.web_port.stopListening()
118         yield self.n.p2p_node.stop()
119         yield self.n.stop()
120         del self.web_port, self.n
121
122 class Test(unittest.TestCase):
123     @defer.inlineCallbacks
124     def test_node(self):
125         n = node.Node(factory, bitcoind, [], [], mynet)
126         yield n.start()
127         
128         wb = work.WorkerBridge(node=n, my_pubkey_hash=42, donation_percentage=2, merged_urls=[], worker_fee=3)
129         web_root = resource.Resource()
130         worker_interface.WorkerInterface(wb).attach_to(web_root)
131         port = reactor.listenTCP(0, server.Site(web_root))
132         
133         proxy = jsonrpc.Proxy('http://127.0.0.1:' + str(port.getHost().port))
134         
135         yield deferral.sleep(3)
136         
137         for i in xrange(100):
138             blah = yield proxy.rpc_getwork()
139             yield proxy.rpc_getwork(blah['data'])
140         
141         yield deferral.sleep(3)
142         
143         assert len(n.tracker.items) == 100
144         assert n.tracker.verified.get_height(n.best_share_var.value) == 100
145         
146         n.stop()
147         
148         yield port.stopListening()
149         del n, wb, web_root, port, proxy
150         import gc
151         gc.collect()
152         gc.collect()
153         gc.collect()
154         
155         yield deferral.sleep(20) # waiting for work_poller to exit
156     #test_node.timeout = 15
157     
158     @defer.inlineCallbacks
159     def test_nodes(self):
160       try:
161         old_successor = data.Share.SUCCESSOR
162         data.Share.SUCCESSOR = data.NewShare
163         
164         N = 3
165         SHARES = 600
166         
167         nodes = []
168         for i in xrange(N):
169             nodes.append((yield MiniNode.start(mynet, factory, bitcoind, [mn.n.p2p_node.serverfactory.listen_port.getHost().port for mn in nodes])))
170         
171         yield deferral.sleep(3)
172         
173         for i in xrange(SHARES):
174             proxy = jsonrpc.Proxy('http://127.0.0.1:' + str(random.choice(nodes).web_port.getHost().port))
175             blah = yield proxy.rpc_getwork()
176             yield proxy.rpc_getwork(blah['data'])
177             yield deferral.sleep(random.expovariate(1/.1))
178             print i
179             print type(nodes[0].n.tracker.items[nodes[0].n.best_share_var.value])
180     
181         yield deferral.sleep(3)
182         
183         for i, n in enumerate(nodes):
184             assert len(n.n.tracker.items) == SHARES, (i, len(n.n.tracker.items))
185             assert n.n.tracker.verified.get_height(n.n.best_share_var.value) == SHARES, (i, n.n.tracker.verified.get_height(n.n.best_share_var.value))
186             assert type(n.n.tracker.items[nodes[0].n.best_share_var.value]) is data.NewShare
187             assert type(n.n.tracker.items[n.n.tracker.get_nth_parent_hash(nodes[0].n.best_share_var.value, SHARES - 5)]) is data.Share
188         
189         for n in nodes:
190             yield n.stop()
191         
192         del nodes, n
193         import gc
194         gc.collect()
195         gc.collect()
196         gc.collect()
197         
198         yield deferral.sleep(20) # waiting for work_poller to exit
199       finally:
200         data.Share.SUCCESSOR = old_successor