modulate share difficulty to prevent any node from producing more than 5% of shares
authorForrest Voight <forrest@forre.st>
Fri, 28 Jun 2013 17:18:33 +0000 (13:18 -0400)
committerForrest Voight <forrest@forre.st>
Fri, 28 Jun 2013 18:26:00 +0000 (14:26 -0400)
p2pool/bitcoin/data.py
p2pool/test/test_node.py
p2pool/work.py

index 8e4fa52..88e1730 100644 (file)
@@ -219,6 +219,10 @@ def target_to_average_attempts(target):
     if target >= 2**256: warnings.warn('target >= 2**256!')
     return 2**256//(target + 1)
 
+def average_attempts_to_target(average_attempts):
+    assert average_attempts > 0
+    return min(int(2**256/average_attempts - 1 + 0.5), 2**256-1)
+
 def target_to_difficulty(target):
     assert 0 <= target and isinstance(target, (int, long)), target
     if target >= 2**256: warnings.warn('target >= 2**256!')
@@ -226,6 +230,7 @@ def target_to_difficulty(target):
 
 def difficulty_to_target(difficulty):
     assert difficulty >= 0
+    if difficulty == 0: return 2**256-1
     return min(int((0xffff0000 * 2**(256-64) + 1)/difficulty - 1 + 0.5), 2**256-1)
 
 # human addresses
index f1f9bea..d1698bc 100644 (file)
@@ -1,5 +1,6 @@
 from __future__ import division
 
+import base64
 import random
 import tempfile
 
@@ -185,7 +186,8 @@ class Test(unittest.TestCase):
         worker_interface.WorkerInterface(wb).attach_to(web_root)
         port = reactor.listenTCP(0, server.Site(web_root))
         
-        proxy = jsonrpc.HTTPProxy('http://127.0.0.1:' + str(port.getHost().port))
+        proxy = jsonrpc.HTTPProxy('http://127.0.0.1:' + str(port.getHost().port),
+            headers=dict(Authorization='Basic ' + base64.b64encode('user/0:password')))
         
         yield deferral.sleep(3)
         
@@ -227,7 +229,8 @@ class Test(unittest.TestCase):
         yield deferral.sleep(3)
         
         for i in xrange(SHARES):
-            proxy = jsonrpc.HTTPProxy('http://127.0.0.1:' + str(random.choice(nodes).web_port.getHost().port))
+            proxy = jsonrpc.HTTPProxy('http://127.0.0.1:' + str(random.choice(nodes).web_port.getHost().port),
+                headers=dict(Authorization='Basic ' + base64.b64encode('user/0:password')))
             blah = yield proxy.rpc_getwork()
             yield proxy.rpc_getwork(blah['data'])
             yield deferral.sleep(.05)
index 40a1a30..346db56 100644 (file)
@@ -142,18 +142,20 @@ class WorkerBridge(worker_interface.WorkerBridge):
         user, contents2 = contents[0], contents[1:]
         
         desired_pseudoshare_target = None
-        desired_share_target = 2**256 - 1
+        desired_share_target = None
         for symbol, parameter in zip(contents2[::2], contents2[1::2]):
             if symbol == '+':
                 try:
                     desired_pseudoshare_target = bitcoin_data.difficulty_to_target(float(parameter))
                 except:
-                    pass
+                    if p2pool.DEBUG:
+                        log.err()
             elif symbol == '/':
                 try:
                     desired_share_target = bitcoin_data.difficulty_to_target(float(parameter))
                 except:
-                    pass
+                    if p2pool.DEBUG:
+                        log.err()
         
         if random.uniform(0, 100) < self.worker_fee:
             pubkey_hash = self.my_pubkey_hash
@@ -169,6 +171,13 @@ class WorkerBridge(worker_interface.WorkerBridge):
         user, pubkey_hash, desired_share_target, desired_pseudoshare_target = self.get_user_details(user)
         return pubkey_hash, desired_share_target, desired_pseudoshare_target
     
+    def _estimate_local_hash_rate(self):
+        if len(self.recent_shares_ts_work) == 50:
+            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])
+            if hash_rate:
+                return hash_rate
+        return None
+    
     def get_work(self, pubkey_hash, desired_share_target, desired_pseudoshare_target):
         if (self.node.p2p_node is None or len(self.node.p2p_node.peers) == 0) and self.node.net.PERSIST:
             raise jsonrpc.Error_for_code(-12345)(u'p2pool is not connected to any peers')
@@ -212,6 +221,13 @@ class WorkerBridge(worker_interface.WorkerBridge):
                 else:
                     share_type = previous_share_type
         
+        if desired_share_target is None:
+            desired_share_target = 2**256-1
+            local_hash_rate = self._estimate_local_hash_rate()
+            if local_hash_rate is not None:
+                desired_share_target = min(desired_share_target,
+                    bitcoin_data.average_attempts_to_target(local_hash_rate * self.node.net.SHARE_PERIOD / 0.05)) # limit to 5% of pool shares by modulating share difficulty
+        
         if True:
             share_info, gentx, other_transaction_hashes, get_share = share_type.generate_transaction(
                 tracker=self.node.tracker,
@@ -249,10 +265,10 @@ class WorkerBridge(worker_interface.WorkerBridge):
         
         if desired_pseudoshare_target is None:
             target = 2**256-1
-            if len(self.recent_shares_ts_work) == 50:
-                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])
-                if hash_rate:
-                    target = min(target, int(2**256/hash_rate))
+            local_hash_rate = self._estimate_local_hash_rate()
+            if local_hash_rate is not None:
+                target = min(target,
+                    bitcoin_data.average_attempts_to_target(local_hash_rate * 1)) # limit to 1 share response every second by modulating pseudoshare difficulty
         else:
             target = desired_pseudoshare_target
         target = max(target, share_info['bits'].target)