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