only cache last 1000 block headers
authorForrest Voight <forrest@forre.st>
Thu, 8 Dec 2011 09:14:21 +0000 (04:14 -0500)
committerForrest Voight <forrest@forre.st>
Thu, 8 Dec 2011 09:14:21 +0000 (04:14 -0500)
p2pool/bitcoin/p2p.py
p2pool/data.py
p2pool/main.py

index 993a08e..d40fe17 100644 (file)
@@ -12,7 +12,7 @@ import time
 from twisted.internet import defer, protocol, reactor, task
 from twisted.python import log
 
-from . import data as bitcoin_data
+from . import data as bitcoin_data, getwork
 from p2pool.util import variable, datachunker, deferral, forest
 
 class TooLong(Exception):
@@ -299,104 +299,80 @@ class HeaderWrapper(object):
 class HeightTracker(object):
     '''Point this at a factory and let it take care of getting block heights'''
     
-    def __init__(self, factory):
-        self.factory = factory
-        self.tracker = forest.Tracker()
-        self.most_recent = None
+    def __init__(self, rpc_proxy, factory):
+        self._rpc_proxy = rpc_proxy
+        self._factory = factory
         
-        self._watch1 = self.factory.new_headers.watch(self.heard_headers)
-        self._watch2 = self.factory.new_block.watch(self.heard_block)
+        self._tracker = forest.Tracker()
         
-        self.requested = set()
-        self._clear_task = task.LoopingCall(self.requested.clear)
+        self._watch1 = self._factory.new_headers.watch(self._heard_headers)
+        self._watch2 = self._factory.new_block.watch(self._heard_block)
+        
+        self._requested = set()
+        self._clear_task = task.LoopingCall(self._requested.clear)
         self._clear_task.start(60)
         
-        self.last_notified_size = 0
+        self._last_notified_size = 0
         
         self.updated = variable.Event()
         
-        self.think()
-    
-    def think(self):
-        highest_head = max(self.tracker.heads, key=lambda h: self.tracker.get_height_and_last(h)[0]) if self.tracker.heads else None
-        height, last = self.tracker.get_height_and_last(highest_head)
-        cur = highest_head
-        cur_height = height
-        have = []
-        step = 1
-        while cur is not None:
-            have.append(cur)
-            if step > cur_height:
-                break
-            cur = self.tracker.get_nth_parent_hash(cur, step)
-            cur_height -= step
-            if len(have) > 10:
-                step *= 2
-        if height:
-            have.append(self.tracker.get_nth_parent_hash(highest_head, height - 1))
-        if not have:
-            have.append(0)
-        self.request(have, None)
-        
-        for tail in self.tracker.tails:
-            if tail is None:
-                continue
-            self.request([], tail)
-        for head in self.tracker.heads:
-            if head == highest_head:
-                continue
-            self.request([head], None)
+        self._think_task = task.LoopingCall(self._think).start(15)
+        self._think2_task = task.LoopingCall(self._think2).start(15)
+    
+    def _think(self):
+        highest_head = max(self._tracker.heads, key=lambda h: self._tracker.get_height_and_last(h)[0]) if self._tracker.heads else None
+        if highest_head is None:
+            return # wait for think2
+        height, last = self._tracker.get_height_and_last(highest_head)
+        if height < 1000:
+            self._request(last)
     
-    def heard_headers(self, headers):
+    @defer.inlineCallbacks
+    def _think2(self):
+        ba = getwork.BlockAttempt.from_getwork((yield self._rpc_proxy.rpc_getwork()))
+        self._request(ba.previous_block)
+    
+    def _heard_headers(self, headers):
         changed = False
         for header in headers:
             hw = HeaderWrapper.from_header(header)
-            if hw.hash in self.tracker.shares:
+            if hw.hash in self._tracker.shares:
                 continue
             changed = True
-            self.tracker.add(hw)
+            self._tracker.add(hw)
         if changed:
             self.updated.happened()
-        self.think()
+        self._think()
         
-        if len(self.tracker.shares) > self.last_notified_size + 10:
-            print 'Have %i block headers' % len(self.tracker.shares)
-            self.last_notified_size = len(self.tracker.shares)
+        if len(self._tracker.shares) >= self._last_notified_size + 100:
+            print 'Have %i block headers' % len(self._tracker.shares)
+            self._last_notified_size = len(self._tracker.shares)
     
-    def heard_block(self, block_hash):
-        self.request([], block_hash)
+    def _heard_block(self, block_hash):
+        self._request(block_hash)
     
     @defer.inlineCallbacks
-    def request(self, have, last):
-        if (tuple(have), last) in self.requested:
+    def _request(self, last):
+        if last in self._tracker.shares:
             return
-        self.requested.add((tuple(have), last))
-        (yield self.factory.getProtocol()).send_getheaders(version=1, have=have, last=last)
-    
-    def getHeight(self, block_hash):
-        height, last = self.tracker.get_height_and_last(block_hash)
-        if last is not None:
-            #self.request([], last)
-            raise ValueError()
-        return height
-    
-    def get_min_height(self, block_hash):
-        height, last = self.tracker.get_height_and_last(block_hash)
-        #if last is not None:
-        #    self.request([], last)
-        return height
-    
-    def get_dist_below_highest(self, block_hash):
-        pass
-        # 0, 1, 2, 3, 4
-    
-    def get_highest_height(self):
-        return self.tracker.get_highest_height()
-    
+        if last in self._requested:
+            return
+        self._requested.add(last)
+        (yield self._factory.getProtocol()).send_getheaders(version=1, have=[], last=last)
+    
+    def get_height_rel_highest(self, block_hash):
+        # callers: highest height can change during yields!
+        height, last = self._tracker.get_height_and_last(block_hash)
+        if last not in self._tracker.tails:
+            return -1e300
+        return height - max(self._tracker.get_height(head_hash) for head_hash in self._tracker.tails[last])
+     
     def stop(self):
-        self.factory.new_headers.unwatch(self._watch1)
-        self.factory.new_block.unwatch(self._watch2)
+        self._factory.new_headers.unwatch(self._watch1)
+        self._factory.new_block.unwatch(self._watch2)
         self._clear_task.stop()
+        self._think_task.stop()
+        self._think2_task.stop()
 
 if __name__ == '__main__':
     factory = ClientFactory(bitcoin_data.BitcoinMainnet)
index 6d048cb..e0b28a1 100644 (file)
@@ -324,7 +324,7 @@ class OkayTracker(forest.Tracker):
         scores = sorted(self.verified.tails.get(best_tail, []), key=lambda h: (
             self.verified.get_work(self.verified.get_nth_parent_hash(h, min(5, self.verified.get_height(h)))),
             #self.verified.shares[h].peer is None,
-            ht.get_min_height(self.verified.shares[h].previous_block),
+            ht.get_height_rel_highest(self.verified.shares[h].previous_block),
             -self.verified.shares[h].time_seen
         ))
         
@@ -337,7 +337,7 @@ class OkayTracker(forest.Tracker):
                 print '   ', format_hash(h), format_hash(self.verified.shares[h].previous_hash), (
                     self.verified.get_work(self.verified.get_nth_parent_hash(h, min(5, self.verified.get_height(h)))),
                     self.verified.shares[h].peer is None,
-                    ht.get_min_height(self.verified.shares[h].previous_block),
+                    ht.get_height_rel_highest(self.verified.shares[h].previous_block),
                     -self.verified.shares[h].time_seen
                 )
         
@@ -393,7 +393,7 @@ class OkayTracker(forest.Tracker):
         
         if best is not None:
             best_share = self.verified.shares[best]
-            if ht.get_min_height(best_share.header['previous_block']) < ht.get_min_height(previous_block) and best_share.header_hash != previous_block and best_share.peer is not None:
+            if ht.get_height_rel_highest(best_share.header['previous_block']) < ht.get_height_rel_highest(previous_block) and best_share.header_hash != previous_block and best_share.peer is not None:
                 if p2pool.DEBUG:
                     print 'Stale detected! %x < %x' % (best_share.header['previous_block'], previous_block)
                 best = best_share.previous_hash
@@ -407,8 +407,8 @@ class OkayTracker(forest.Tracker):
         block_height = 0
         max_height = min(self.net.CHAIN_LENGTH, head_height)
         for share in reversed(list(itertools.islice(self.verified.get_chain_known(self.verified.get_nth_parent_hash(share_hash, max_height//2)), max_height//2))):
-            block_height = max(block_height, ht.get_min_height(share.header['previous_block']))
-            this_score = (self.verified.get_work(share_hash) - self.verified.get_work(share.hash))//(ht.get_highest_height() - block_height + 1)
+            block_height = max(block_height, ht.get_height_rel_highest(share.header['previous_block']))
+            this_score = (self.verified.get_work(share_hash) - self.verified.get_work(share.hash))//(0 - block_height + 1)
             if this_score > score2:
                 score2 = this_score
         return min(head_height, self.net.CHAIN_LENGTH), score2
index ade2873..18b850f 100644 (file)
@@ -94,7 +94,7 @@ def main(args, net):
         print '    Payout script:', bitcoin_data.script2_to_human(my_script, net)
         print
         
-        ht = bitcoin_p2p.HeightTracker(factory)
+        ht = bitcoin_p2p.HeightTracker(bitcoind, factory)
         
         tracker = p2pool_data.OkayTracker(net)
         shared_share_hashes = set()