From c345d5419ba3a5917821e543f9fe53895c2875af Mon Sep 17 00:00:00 2001 From: Forrest Voight Date: Wed, 3 Jul 2013 12:33:33 -0400 Subject: [PATCH] dynamically adjust share difficulty to prevent payouts below dust threshold --- p2pool/bitcoin/networks.py | 8 ++++++++ p2pool/main.py | 3 ++- p2pool/web.py | 16 +++------------- p2pool/work.py | 32 +++++++++++++++++++++++++++++++- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/p2pool/bitcoin/networks.py b/p2pool/bitcoin/networks.py index 605d5d2..15b7841 100644 --- a/p2pool/bitcoin/networks.py +++ b/p2pool/bitcoin/networks.py @@ -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, ), ) diff --git a/p2pool/main.py b/p2pool/main.py index cbb3769..aae66ae 100644 --- a/p2pool/main.py +++ b/p2pool/main.py @@ -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: diff --git a/p2pool/web.py b/p2pool/web.py index 2cb7ee8..e3c6330 100644 --- a/p2pool/web.py +++ b/p2pool/web.py @@ -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)) diff --git a/p2pool/work.py b/p2pool/work.py index 9f8b119..f0a989d 100644 --- a/p2pool/work.py +++ b/p2pool/work.py @@ -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 -- 1.7.1