moved heighttracker to bitcoin.height_tracker
[p2pool.git] / p2pool / main.py
index d959e28..379522f 100644 (file)
@@ -5,7 +5,6 @@ import StringIO
 import argparse
 import os
 import random
-import struct
 import sys
 import time
 import signal
@@ -18,7 +17,7 @@ from twisted.python import log
 from nattraverso import portmapper, ipdiscover
 
 import bitcoin.p2p as bitcoin_p2p, bitcoin.getwork as bitcoin_getwork, bitcoin.data as bitcoin_data
-from bitcoin import worker_interface
+from bitcoin import worker_interface, height_tracker
 from util import expiring_dict, jsonrpc, variable, deferral, math, logging, pack
 from . import p2p, networks, web
 import p2pool, p2pool.data as p2pool_data
@@ -167,16 +166,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
             ))
         yield set_real_work1()
         
-        if '\ngetblock ' in (yield deferral.retry()(bitcoind.rpc_help)()):
-            height_cacher = deferral.DeferredCacher(defer.inlineCallbacks(lambda block_hash: defer.returnValue((lambda x: x['blockcount'] if 'blockcount' in x else x['height'])((yield bitcoind.rpc_getblock('%x' % (block_hash,)))))))
-            best_height_cached = variable.Variable((yield deferral.retry()(height_cacher)(pre_current_work.value['previous_block'])))
-            def get_height_rel_highest(block_hash):
-                this_height = height_cacher.call_now(block_hash, 0)
-                best_height = height_cacher.call_now(pre_current_work.value['previous_block'], 0)
-                best_height_cached.set(max(best_height_cached.value, this_height, best_height))
-                return this_height - best_height_cached.value
-        else:
-            get_height_rel_highest = bitcoin_p2p.HeightTracker(bitcoind, factory, 5*net.SHARE_PERIOD*net.CHAIN_LENGTH/net.PARENT.BLOCK_PERIOD).get_height_rel_highest
+        get_height_rel_highest = yield height_tracker.get_height_rel_highest_func(bitcoind, factory, pre_current_work, net)
         
         def set_real_work2():
             best, desired = tracker.think(get_height_rel_highest, pre_current_work.value['previous_block'], pre_current_work.value['bits'])
@@ -296,13 +286,18 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                 print 'Sending %i shares to %s:%i' % (len(shares), peer.addr[0], peer.addr[1])
                 peer.sendShares(shares)
         
-        
-        submit_block = deferral.retry('Error submitting primary block: (will retry)', 10, 10)(lambda block: bitcoind.rpc_getmemorypool(bitcoin_data.block_type.pack(block).encode('hex')))
+        @deferral.retry('Error submitting block: (will retry)', 10, 10)
+        @defer.inlineCallbacks
+        def submit_block(block, ignore_failure):
+            success = yield bitcoind.rpc_getmemorypool(bitcoin_data.block_type.pack(block).encode('hex'))
+            success_expected = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(block['header'])) <= block['header']['bits'].target
+            if (not success and success_expected and not ignore_failure) or (success and not success_expected):
+                print >>sys.stderr, 'Block submittal result: %s Expected: %s' % (result, expected_result)
         
         @tracker.verified.added.watch
         def _(share):
             if share.pow_hash <= share.header['bits'].target:
-                submit_block(share.as_block(tracker))
+                submit_block(share.as_block(tracker), ignore_failure=True)
                 print
                 print 'GOT BLOCK FROM PEER! Passing to bitcoind! %s bitcoin: %s%064x' % (p2pool_data.format_hash(share.hash), net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash)
                 print
@@ -496,54 +491,31 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                     mm_data = ''
                     mm_later = []
                 
-                new = (tracker.shares[current_work.value['best_share_hash']].timestamp if current_work.value['best_share_hash'] is not None else time.time()) > net.SWITCH_TIME
-                
-                if new:
-                    share_info, generate_tx = p2pool_data.new_generate_transaction(
-                        tracker=tracker,
-                        share_data=dict(
-                            previous_share_hash=current_work.value['best_share_hash'],
-                            coinbase=(mm_data + current_work.value['coinbaseflags'])[:100],
-                            nonce=random.randrange(2**32),
-                            pubkey_hash=pubkey_hash,
-                            subsidy=current_work2.value['subsidy'],
-                            donation=math.perfect_round(65535*args.donation_percentage/100),
-                            stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain):
-                                253 if orphans > orphans_recorded_in_chain else
-                                254 if doas > doas_recorded_in_chain else
-                                0
-                            )(*get_stale_counts()),
-                        ),
-                        block_target=current_work.value['bits'].target,
-                        desired_timestamp=int(time.time() - current_work2.value['clock_offset']),
-                        desired_target=desired_share_target,
-                        net=net,
-                    )
-                else:
-                    share_info, generate_tx = p2pool_data.generate_transaction(
-                        tracker=tracker,
-                        share_data=dict(
-                            previous_share_hash=current_work.value['best_share_hash'],
-                            coinbase=(mm_data + current_work.value['coinbaseflags'])[:100],
-                            nonce=struct.pack('<Q', random.randrange(2**64)),
-                            new_script=bitcoin_data.pubkey_hash_to_script2(pubkey_hash),
-                            subsidy=current_work2.value['subsidy'],
-                            donation=math.perfect_round(65535*args.donation_percentage/100),
-                            stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain):
-                                253 if orphans > orphans_recorded_in_chain else
-                                254 if doas > doas_recorded_in_chain else
-                                0
-                            )(*get_stale_counts()),
-                        ),
-                        block_target=current_work.value['bits'].target,
-                        desired_timestamp=int(time.time() - current_work2.value['clock_offset']),
-                        net=net,
-                    )
+                share_info, generate_tx = p2pool_data.generate_transaction(
+                    tracker=tracker,
+                    share_data=dict(
+                        previous_share_hash=current_work.value['best_share_hash'],
+                        coinbase=(mm_data + current_work.value['coinbaseflags'])[:100],
+                        nonce=random.randrange(2**32),
+                        pubkey_hash=pubkey_hash,
+                        subsidy=current_work2.value['subsidy'],
+                        donation=math.perfect_round(65535*args.donation_percentage/100),
+                        stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain):
+                            253 if orphans > orphans_recorded_in_chain else
+                            254 if doas > doas_recorded_in_chain else
+                            0
+                        )(*get_stale_counts()),
+                    ),
+                    block_target=current_work.value['bits'].target,
+                    desired_timestamp=int(time.time() - current_work2.value['clock_offset']),
+                    desired_target=desired_share_target,
+                    net=net,
+                )
                 
                 target = net.PARENT.SANE_MAX_TARGET
                 if desired_pseudoshare_target is None:
                     if len(self.recent_shares_ts_work) == 50:
-                        hash_rate = sum(work for ts, work in self.recent_shares_ts_work)//(self.recent_shares_ts_work[-1][0] - self.recent_shares_ts_work[0][0])
+                        hash_rate = sum(work for ts, work in self.recent_shares_ts_work[1:])//(self.recent_shares_ts_work[-1][0] - self.recent_shares_ts_work[0][0])
                         target = min(target, 2**256//hash_rate)
                 else:
                     target = min(target, desired_pseudoshare_target)
@@ -585,7 +557,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                     
                     try:
                         if pow_hash <= header['bits'].target or p2pool.DEBUG:
-                            submit_block(dict(header=header, txs=transactions))
+                            submit_block(dict(header=header, txs=transactions), ignore_failure=False)
                             if pow_hash <= header['bits'].target:
                                 print
                                 print 'GOT BLOCK FROM MINER! Passing to bitcoind! %s%064x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, header_hash)
@@ -624,12 +596,10 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                             log.err(None, 'Error while processing merged mining POW:')
                     
                     if pow_hash <= share_info['bits'].target:
-                        if new:
-                            min_header = dict(header);del min_header['merkle_root']
-                            hash_link = p2pool_data.prefix_to_hash_link(packed_generate_tx[:-32-4], p2pool_data.gentx_before_refhash)
-                            share = p2pool_data.NewShare(net, None, min_header, share_info, hash_link=hash_link, merkle_branch=merkle_branch, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
-                        else:
-                            share = p2pool_data.Share(net, None, header, share_info, merkle_branch=merkle_branch, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
+                        min_header = dict(header);del min_header['merkle_root']
+                        hash_link = p2pool_data.prefix_to_hash_link(packed_generate_tx[:-32-4], p2pool_data.gentx_before_refhash)
+                        share = p2pool_data.Share(net, None, min_header, share_info, hash_link=hash_link, merkle_branch=merkle_branch, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
+                        
                         print 'GOT SHARE! %s %s prev %s age %.2fs%s' % (
                             request.getUser(),
                             p2pool_data.format_hash(share.hash),
@@ -654,22 +624,21 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                         except:
                             log.err(None, 'Error forwarding block solution:')
                     
-                    if pow_hash <= target and header_hash not in received_header_hashes:
+                    if pow_hash > target:
+                        print 'Worker %s submitted share with hash > target:' % (request.getUser(),)
+                        print '    Hash:   %56x' % (pow_hash,)
+                        print '    Target: %56x' % (target,)
+                    elif header_hash in received_header_hashes:
+                        print >>sys.stderr, 'Worker %s @ %s submitted share more than once!' % (request.getUser(), request.getClientIP())
+                    else:
+                        received_header_hashes.add(header_hash)
+                        
                         pseudoshare_received.happened(bitcoin_data.target_to_average_attempts(target), not on_time, request.getUser() if request.getPassword() == vip_pass else None)
                         self.recent_shares_ts_work.append((time.time(), bitcoin_data.target_to_average_attempts(target)))
                         while len(self.recent_shares_ts_work) > 50:
                             self.recent_shares_ts_work.pop(0)
                         local_rate_monitor.add_datum(dict(work=bitcoin_data.target_to_average_attempts(target), dead=not on_time, user=request.getUser()))
                     
-                    if header_hash in received_header_hashes:
-                        print >>sys.stderr, 'Worker %s @ %s submitted share more than once!' % (request.getUser(), request.getClientIP())
-                    received_header_hashes.add(header_hash)
-                    
-                    if pow_hash > target:
-                        print 'Worker %s submitted share with hash > target:' % (request.getUser(),)
-                        print '    Hash:   %56x' % (pow_hash,)
-                        print '    Target: %56x' % (target,)
-                    
                     return on_time
                 
                 return ba, got_response
@@ -734,7 +703,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                     if share.pow_hash <= share.header['bits'].target and share.header_hash not in self.announced_hashes and abs(share.timestamp - time.time()) < 10*60:
                         self.announced_hashes.add(share.header_hash)
                         message = '\x02%s BLOCK FOUND by %s! %s%064x' % (net.NAME.upper(), bitcoin_data.script2_to_address(share.new_script, net.PARENT), net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash)
-                        self.delayed_messages[message] = reactor.callLater(random.expovariate(1/5), lambda: (self.say(self.channel, message), self.delayed_messages.pop(message)))
+                        self.delayed_messages[message] = reactor.callLater(random.expovariate(1/60), lambda: (self.say(self.channel, message), self.delayed_messages.pop(message)))
                 def connectionLost(self, reason):
                     tracker.verified.added.unwatch(self.watch_id)
                     print 'IRC connection lost:', reason.getErrorMessage()