dynamically adjust share difficulty to prevent payouts below dust threshold
authorForrest Voight <forrest@forre.st>
Wed, 3 Jul 2013 16:33:33 +0000 (12:33 -0400)
committerForrest Voight <forrest@forre.st>
Wed, 3 Jul 2013 17:47:48 +0000 (13:47 -0400)
p2pool/bitcoin/networks.py
p2pool/main.py
p2pool/web.py
p2pool/work.py

index 605d5d2..15b7841 100644 (file)
@@ -25,6 +25,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://blockexplorer.com/address/',
         SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
         DUMB_SCRYPT_DIFF=1,
+        DUST_THRESHOLD=0.001e8,
     ),
     bitcoin_testnet=math.Object(
         P2P_PREFIX='0b110907'.decode('hex'),
@@ -44,6 +45,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://blockexplorer.com/testnet/address/',
         SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
         DUMB_SCRYPT_DIFF=1,
+        DUST_THRESHOLD=1e8,
     ),
     
     namecoin=math.Object(
@@ -64,6 +66,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://explorer.dot-bit.org/a/',
         SANE_TARGET_RANGE=(2**256//2**32 - 1, 2**256//2**32 - 1),
         DUMB_SCRYPT_DIFF=1,
+        DUST_THRESHOLD=0.2e8,
     ),
     namecoin_testnet=math.Object(
         P2P_PREFIX='fabfb5fe'.decode('hex'),
@@ -83,6 +86,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://testnet.explorer.dot-bit.org/a/',
         SANE_TARGET_RANGE=(2**256//2**32 - 1, 2**256//2**32 - 1),
         DUMB_SCRYPT_DIFF=1,
+        DUST_THRESHOLD=1e8,
     ),
     
     litecoin=math.Object(
@@ -103,6 +107,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://explorer.litecoin.net/address/',
         SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256//1000 - 1),
         DUMB_SCRYPT_DIFF=2**16,
+        DUST_THRESHOLD=0.03e8,
     ),
     litecoin_testnet=math.Object(
         P2P_PREFIX='fcc1b7dc'.decode('hex'),
@@ -122,6 +127,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://nonexistent-litecoin-testnet-explorer/address/',
         SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256 - 1),
         DUMB_SCRYPT_DIFF=2**16,
+        DUST_THRESHOLD=1e8,
     ),
 
     terracoin=math.Object(
@@ -142,6 +148,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/address/',
         SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
         DUMB_SCRYPT_DIFF=1,
+        DUST_THRESHOLD=1e8,
     ),
     terracoin_testnet=math.Object(
         P2P_PREFIX='41babe56'.decode('hex'),
@@ -161,6 +168,7 @@ nets = dict(
         ADDRESS_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/testnet/address/',
         SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1),
         DUMB_SCRYPT_DIFF=1,
+        DUST_THRESHOLD=1e8,
     ),
 
 )
index cbb3769..aae66ae 100644 (file)
@@ -311,11 +311,12 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                     
                     datums, dt = wb.local_rate_monitor.get_datums_in_last()
                     my_att_s = sum(datum['work']/dt for datum in datums)
+                    my_shares_per_s = sum(datum['work']/dt/bitcoin_data.target_to_average_attempts(datum['share_target']) for datum in datums)
                     this_str += '\n Local: %sH/s in last %s Local dead on arrival: %s Expected time to share: %s' % (
                         math.format(int(my_att_s)),
                         math.format_dt(dt),
                         math.format_binomial_conf(sum(1 for datum in datums if datum['dead']), len(datums), 0.95),
-                        math.format_dt(2**256 / node.tracker.items[node.best_share_var.value].max_target / my_att_s) if my_att_s and node.best_share_var.value else '???',
+                        math.format_dt(1/my_shares_per_s) if my_shares_per_s else '???',
                     )
                     
                     if height > 2:
index 2cb7ee8..e3c6330 100644 (file)
@@ -91,16 +91,6 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
             if bitcoin_data.script2_to_address(script, node.net.PARENT) is not None
         ))
     
-    def get_local_rates():
-        miner_hash_rates = {}
-        miner_dead_hash_rates = {}
-        datums, dt = wb.local_rate_monitor.get_datums_in_last()
-        for datum in datums:
-            miner_hash_rates[datum['user']] = miner_hash_rates.get(datum['user'], 0) + datum['work']/dt
-            if datum['dead']:
-                miner_dead_hash_rates[datum['user']] = miner_dead_hash_rates.get(datum['user'], 0) + datum['work']/dt
-        return miner_hash_rates, miner_dead_hash_rates
-    
     def get_global_stats():
         # averaged over last hour
         if node.tracker.get_height(node.best_share_var.value) < 10:
@@ -138,7 +128,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
             node.tracker.items[node.tracker.get_nth_parent_hash(node.best_share_var.value, lookbehind - 1)].timestamp)
         share_att_s = my_work / actual_time
         
-        miner_hash_rates, miner_dead_hash_rates = get_local_rates()
+        miner_hash_rates, miner_dead_hash_rates = wb.get_local_rates()
         (stale_orphan_shares, stale_doa_shares), shares, _ = wb.get_stale_counts()
         
         return dict(
@@ -251,7 +241,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
         
         global_stale_prop = p2pool_data.get_average_stale_prop(node.tracker, node.best_share_var.value, lookbehind)
         (stale_orphan_shares, stale_doa_shares), shares, _ = wb.get_stale_counts()
-        miner_hash_rates, miner_dead_hash_rates = get_local_rates()
+        miner_hash_rates, miner_dead_hash_rates = wb.get_local_rates()
         
         stat_log.append(dict(
             time=time.time(),
@@ -429,7 +419,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve
         
         current_txouts = node.get_current_txouts()
         hd.datastreams['current_payout'].add_datum(t, current_txouts.get(bitcoin_data.pubkey_hash_to_script2(wb.my_pubkey_hash), 0)*1e-8)
-        miner_hash_rates, miner_dead_hash_rates = get_local_rates()
+        miner_hash_rates, miner_dead_hash_rates = wb.get_local_rates()
         current_txouts_by_address = dict((bitcoin_data.script2_to_address(script, node.net.PARENT), amount) for script, amount in current_txouts.iteritems())
         hd.datastreams['current_payouts'].add_datum(t, dict((user, current_txouts_by_address[user]*1e-8) for user in miner_hash_rates if user in current_txouts_by_address))
         
index 9f8b119..f0a989d 100644 (file)
@@ -31,6 +31,7 @@ class WorkerBridge(worker_interface.WorkerBridge):
         self.pseudoshare_received = variable.Event()
         self.share_received = variable.Event()
         self.local_rate_monitor = math.RateMonitor(10*60)
+        self.local_addr_rate_monitor = math.RateMonitor(10*60)
         
         self.removed_unstales_var = variable.Variable((0, 0, 0))
         self.removed_doa_unstales_var = variable.Variable(0)
@@ -178,6 +179,23 @@ class WorkerBridge(worker_interface.WorkerBridge):
                 return hash_rate
         return None
     
+    def get_local_rates(self):
+        miner_hash_rates = {}
+        miner_dead_hash_rates = {}
+        datums, dt = self.local_rate_monitor.get_datums_in_last()
+        for datum in datums:
+            miner_hash_rates[datum['user']] = miner_hash_rates.get(datum['user'], 0) + datum['work']/dt
+            if datum['dead']:
+                miner_dead_hash_rates[datum['user']] = miner_dead_hash_rates.get(datum['user'], 0) + datum['work']/dt
+        return miner_hash_rates, miner_dead_hash_rates
+    
+    def get_local_addr_rates(self):
+        addr_hash_rates = {}
+        datums, dt = self.local_addr_rate_monitor.get_datums_in_last()
+        for datum in datums:
+            addr_hash_rates[datum['pubkey_hash']] = addr_hash_rates.get(datum['pubkey_hash'], 0) + datum['work']/dt
+        return addr_hash_rates
+    
     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')
@@ -231,6 +249,17 @@ class WorkerBridge(worker_interface.WorkerBridge):
             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
+            
+            local_addr_rates = self.get_local_addr_rates()
+            lookbehind = 3600//self.node.net.SHARE_PERIOD
+            block_subsidy = self.node.bitcoind_work.value['subsidy']
+            if self.node.tracker.get_height(previous_share.hash) > lookbehind:
+                expected_payout_per_block = local_addr_rates.get(pubkey_hash, 0)/p2pool_data.get_pool_attempts_per_second(self.node.tracker, self.node.best_share_var.value, lookbehind) \
+                    * block_subsidy*(1-self.donation_percentage/100) # XXX doesn't use global stale rate to compute pool hash
+                if expected_payout_per_block < self.node.net.PARENT.DUST_THRESHOLD:
+                    desired_share_target = min(desired_share_target,
+                        bitcoin_data.average_attempts_to_target((bitcoin_data.target_to_average_attempts(self.node.bitcoind_work.value['bits'].target)*self.node.net.SPREAD)*self.node.net.PARENT.DUST_THRESHOLD/block_subsidy)
+                    )
         
         if True:
             share_info, gentx, other_transaction_hashes, get_share = share_type.generate_transaction(
@@ -393,7 +422,8 @@ class WorkerBridge(worker_interface.WorkerBridge):
                 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)
-                self.local_rate_monitor.add_datum(dict(work=bitcoin_data.target_to_average_attempts(target), dead=not on_time, user=user))
+                self.local_rate_monitor.add_datum(dict(work=bitcoin_data.target_to_average_attempts(target), dead=not on_time, user=user, share_target=share_info['bits'].target))
+                self.local_addr_rate_monitor.add_datum(dict(work=bitcoin_data.target_to_average_attempts(target), pubkey_hash=pubkey_hash))
             
             return on_time