class WeightsSkipList(forest.TrackerSkipList): # share_count, weights, total_weight def get_delta(self, element): from p2pool.bitcoin import data as bitcoin_data share = self.tracker.items[element] att = bitcoin_data.target_to_average_attempts(share.target) return 1, {share.new_script: att*(65535-share.share_data['donation'])}, att*65535, att*share.share_data['donation'] def combine_deltas(self, (share_count1, weights1, total_weight1, total_donation_weight1), (share_count2, weights2, total_weight2, total_donation_weight2)): return share_count1 + share_count2, math.add_dicts(weights1, weights2), total_weight1 + total_weight2, total_donation_weight1 + total_donation_weight2
new_weights = {script: (desired_weight - total_weight1)//65535*weights2[script]//(total_weight2//65535)} return share_count1 + share_count2, (weights_list, new_weights), desired_weight, total_donation_weight1 + (desired_weight - total_weight1)//65535*total_donation_weight2//(total_weight2//65535) return share_count1 + share_count2, (weights_list, weights2), total_weight1 + total_weight2, total_donation_weight1 + total_donation_weight2 def judge(self, (share_count, weights_list, total_weight, total_donation_weight), (max_shares, desired_weight)): if share_count > max_shares or total_weight > desired_weight: return 1 elif share_count == max_shares or total_weight == desired_weight: return 0 else: return -1 def finalize(self, (share_count, weights_list, total_weight, total_donation_weight), (max_shares, desired_weight)): assert share_count <= max_shares and total_weight <= desired_weight assert share_count == max_shares or total_weight == desired_weight return math.add_dicts(*math.flatten_linked_list(weights_list)), total_weight, total_donation_weight class OkayTracker(forest.Tracker): def __init__(self, net): forest.Tracker.__init__(self, delta_type=forest.get_attributedelta_type(dict(forest.AttributeDelta.attrs, work=lambda share: bitcoin_data.target_to_average_attempts(share.target), min_work=lambda share: bitcoin_data.target_to_average_attempts(share.max_target), ))) self.net = net self.verified = forest.SubsetTracker(delta_type=forest.get_attributedelta_type(dict(forest.AttributeDelta.attrs, work=lambda share: bitcoin_data.target_to_average_attempts(share.target), )), subset_of=self) self.get_cumulative_weights = WeightsSkipList(self) def attempt_verify(self, share): if share.hash in self.verified.items:
def generate_transaction(tracker, share_data, block_target, desired_timestamp, net): previous_share_hash = share_data['previous_share_hash'] new_script = share_data['new_script'] subsidy = share_data['subsidy'] donation = share_data['donation'] assert 0 <= donation <= 65535 if len(share_data['coinbase']) > 100: raise ValueError('coinbase too long!') previous_share = tracker.shares[previous_share_hash] if previous_share_hash is not None else None chain_length = getattr(net, 'REAL_CHAIN_LENGTH_FUNC', lambda _: net.REAL_CHAIN_LENGTH)(previous_share.timestamp if previous_share is not None else None) height, last = tracker.get_height_and_last(previous_share_hash) assert height >= chain_length or last is None if height < net.TARGET_LOOKBEHIND: bits = bitcoin_data.FloatingInteger.from_target_upper_bound(net.MAX_TARGET) else: attempts_per_second = get_pool_attempts_per_second(tracker, previous_share_hash, net.TARGET_LOOKBEHIND) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 pre_target2 = math.clip(pre_target, (previous_share.target*9//10, previous_share.target*11//10)) pre_target3 = math.clip(pre_target2, (0, net.MAX_TARGET)) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) attempts_to_block = bitcoin_data.target_to_average_attempts(block_target) max_att = net.SPREAD * attempts_to_block this_att = min(bitcoin_data.target_to_average_attempts(bits.target), max_att) other_weights, other_total_weight, other_donation_weight = tracker.get_cumulative_weights(previous_share_hash, min(height, chain_length), 65535*max(0, max_att - this_att)) assert other_total_weight == sum(other_weights.itervalues()) + other_donation_weight, (other_total_weight, sum(other_weights.itervalues()) + other_donation_weight) weights, total_weight, donation_weight = math.add_dicts({new_script: this_att*(65535-donation)}, other_weights), this_att*65535 + other_total_weight, this_att*donation + other_donation_weight assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) SCRIPT = '4104ffd03de44a6e11b9917f3a29f9443283d9871c9d743ef30d5eddcd37094b64d1b3d8090496b53256786bf5c82932ec23c3b74d9f05a6f95a8b5529352656664bac'.decode('hex') # 1 satoshi is always donated so that a list of p2pool generated blocks can be easily found by looking at the donation address amounts = dict((script, (subsidy-1)*(199*weight)//(200*total_weight)) for (script, weight) in weights.iteritems()) amounts[new_script] = amounts.get(new_script, 0) + (subsidy-1)//200 amounts[SCRIPT] = amounts.get(SCRIPT, 0) + (subsidy-1)*(199*donation_weight)//(200*total_weight) amounts[SCRIPT] = amounts.get(SCRIPT, 0) + subsidy - sum(amounts.itervalues()) # collect any extra satoshis :P if sum(amounts.itervalues()) != subsidy: raise ValueError() if any(x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted(amounts.iterkeys(), key=lambda script: (amounts[script], script)) dests = dests[-4000:] # block length limit, unlikely to ever be hit share_info = dict( share_data=share_data, bits=bits, timestamp=math.clip(desired_timestamp, (previous_share.timestamp - 60, previous_share.timestamp + 60)) if previous_share is not None else desired_timestamp, ) return share_info, dict( version=1, tx_ins=[dict( previous_output=None, sequence=None, script=share_data['coinbase'].ljust(2, '\x00'), )], tx_outs=[dict(value=0, script='\x20' + pack.IntType(256).pack(bitcoin_data.hash256(share_info_type.pack(share_info))))] + [dict(value=amounts[script], script=script) for script in dests if amounts[script]], lock_time=0, )
def combine_deltas(self, (share_count1, weights1, total_weight1, total_donation_weight1), (share_count2, weights2, total_weight2, total_donation_weight2)): return share_count1 + share_count2, math.add_dicts([weights1, weights2]), total_weight1 + total_weight2, total_donation_weight1 + total_donation_weight2 def initial_solution(self, start, (max_shares, desired_weight)): assert desired_weight % 65535 == 0, divmod(desired_weight, 65535) return 0, {}, 0, 0 def apply_delta(self, (share_count1, weights1, total_weight1, total_donation_weight1), (share_count2, weights2, total_weight2, total_donation_weight2), (max_shares, desired_weight)): if total_weight1 + total_weight2 > desired_weight and share_count2 == 1: script, = weights2.iterkeys() new_weights = dict(weights1) assert (desired_weight - total_weight1) % 65535 == 0 new_weights[script] = new_weights.get(script, 0) + (desired_weight - total_weight1)//65535*weights2[script]//(total_weight2//65535) return share_count1 + share_count2, new_weights, desired_weight, total_donation_weight1 + (desired_weight - total_weight1)//65535*total_donation_weight2//(total_weight2//65535) return share_count1 + share_count2, math.add_dicts([weights1, weights2]), total_weight1 + total_weight2, total_donation_weight1 + total_donation_weight2 def judge(self, (share_count, weights, total_weight, total_donation_weight), (max_shares, desired_weight)): if share_count > max_shares or total_weight > desired_weight: return 1 elif share_count == max_shares or total_weight == desired_weight: return 0 else: return -1 def finalize(self, (share_count, weights, total_weight, total_donation_weight)): return weights, total_weight, total_donation_weight class CountsSkipList(skiplist.SkipList): # share_count, counts, total_count
def generate_transaction(tracker, previous_share_hash, new_script, subsidy, nonce, block_target, net): height, last = tracker.get_height_and_last(previous_share_hash) if height < net.TARGET_LOOKBEHIND: target = bitcoin_data.FloatingIntegerType().truncate_to(2**256//2**20 - 1) else: attempts_per_second = get_pool_attempts_per_second(tracker, previous_share_hash, net) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 previous_share = tracker.shares[previous_share_hash] if previous_share_hash is not None else None pre_target2 = math.clip(pre_target, (previous_share.target*9//10, previous_share.target*11//10)) pre_target3 = math.clip(pre_target2, (0, net.MAX_TARGET)) target = bitcoin_data.FloatingIntegerType().truncate_to(pre_target3) attempts_to_block = bitcoin_data.target_to_average_attempts(block_target) max_weight = net.SPREAD * attempts_to_block ''' class fake_share(object): pass fake_share.new_script = new_script fake_share.target = target dest_weights = {} total_weight = 0 for share in itertools.chain([fake_share], itertools.islice(tracker.get_chain_to_root(previous_share_hash), net.CHAIN_LENGTH)): weight = bitcoin_data.target_to_average_attempts(share.target) if weight > max_weight - total_weight: weight = max_weight - total_weight dest_weights[share.new_script] = dest_weights.get(share.new_script, 0) + weight total_weight += weight if total_weight == max_weight: break ''' this_weight = min(bitcoin_data.target_to_average_attempts(target), max_weight) other_weights, other_weights_total = tracker.get_cumulative_weights(previous_share_hash, min(height, net.CHAIN_LENGTH), max(0, max_weight - this_weight)) dest_weights, total_weight = math.add_dicts([{new_script: this_weight}, other_weights]), this_weight + other_weights_total total_weight = sum(dest_weights.itervalues()) amounts = dict((script, subsidy*(396*weight)//(400*total_weight)) for (script, weight) in dest_weights.iteritems()) amounts[new_script] = amounts.get(new_script, 0) + subsidy*2//400 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy*2//400 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy - sum(amounts.itervalues()) # collect any extra if sum(amounts.itervalues()) != subsidy: raise ValueError() if any(x < 0 for x in amounts.itervalues()): raise ValueError() pre_dests = sorted(amounts.iterkeys(), key=lambda script: (amounts[script], script)) pre_dests = pre_dests[-4000:] # block length limit, unlikely to ever be hit dests = sorted(pre_dests, key=lambda script: (script == new_script, script)) assert dests[-1] == new_script return dict( version=1, tx_ins=[dict( previous_output=None, sequence=None, script=coinbase_type.pack(dict( identifier=net.IDENTIFIER, share_data=dict( previous_share_hash=previous_share_hash, nonce=nonce, target=target, ), )), )], tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script]], lock_time=0, )
total_donation_weight1), (share_count2, weights2, total_weight2, total_donation_weight2), (max_shares, desired_weight)): if total_weight1 + total_weight2 > desired_weight and share_count2 == 1: script, = weights2.iterkeys() new_weights = dict(weights1) assert (desired_weight - total_weight1) % 65535 == 0 new_weights[script] = new_weights.get( script, 0) + (desired_weight - total_weight1 ) // 65535 * weights2[script] // (total_weight2 // 65535) return share_count1 + share_count2, new_weights, desired_weight, total_donation_weight1 + ( desired_weight - total_weight1 ) // 65535 * total_donation_weight2 // (total_weight2 // 65535) return share_count1 + share_count2, math.add_dicts( [weights1, weights2] ), total_weight1 + total_weight2, total_donation_weight1 + total_donation_weight2 def judge(self, (share_count, weights, total_weight, total_donation_weight), (max_shares, desired_weight)): if share_count > max_shares or total_weight > desired_weight: return 1 elif share_count == max_shares or total_weight == desired_weight: return 0 else: return -1 def finalize(self, (share_count, weights, total_weight, total_donation_weight)): return weights, total_weight, total_donation_weight
def generate_transaction(tracker, share_data, block_target, desired_timestamp, net): previous_share_hash = share_data['previous_share_hash'] new_script = share_data['new_script'] subsidy = share_data['subsidy'] donation = share_data['donation'] assert 0 <= donation <= 65535 if len(share_data['coinbase']) > 100: raise ValueError('coinbase too long!') previous_share = tracker.shares[ previous_share_hash] if previous_share_hash is not None else None chain_length = getattr( net, 'REAL_CHAIN_LENGTH_FUNC', lambda _: net.REAL_CHAIN_LENGTH)( previous_share.timestamp if previous_share is not None else None) height, last = tracker.get_height_and_last(previous_share_hash) assert height >= chain_length or last is None if height < net.TARGET_LOOKBEHIND: target = bitcoin_data.FloatingInteger.from_target_upper_bound( net.MAX_TARGET) else: attempts_per_second = get_pool_attempts_per_second( tracker, previous_share_hash, net.TARGET_LOOKBEHIND) pre_target = 2**256 // (net.SHARE_PERIOD * attempts_per_second) - 1 pre_target2 = math.clip(pre_target, (previous_share.target * 9 // 10, previous_share.target * 11 // 10)) pre_target3 = math.clip(pre_target2, (0, net.MAX_TARGET)) target = bitcoin_data.FloatingInteger.from_target_upper_bound( pre_target3) attempts_to_block = bitcoin_data.target_to_average_attempts(block_target) max_att = net.SPREAD * attempts_to_block this_att = min(bitcoin_data.target_to_average_attempts(target), max_att) other_weights, other_total_weight, other_donation_weight = tracker.get_cumulative_weights( previous_share_hash, min(height, chain_length), 65535 * max(0, max_att - this_att)) assert other_total_weight == sum( other_weights.itervalues()) + other_donation_weight, ( other_total_weight, sum(other_weights.itervalues()) + other_donation_weight) weights, total_weight, donation_weight = math.add_dicts( [{ new_script: this_att * (65535 - donation) }, other_weights] ), this_att * 65535 + other_total_weight, this_att * donation + other_donation_weight assert total_weight == sum(weights.itervalues()) + donation_weight, ( total_weight, sum(weights.itervalues()) + donation_weight) SCRIPT = '4104ffd03de44a6e11b9917f3a29f9443283d9871c9d743ef30d5eddcd37094b64d1b3d8090496b53256786bf5c82932ec23c3b74d9f05a6f95a8b5529352656664bac'.decode( 'hex') # 1 satoshi is always donated so that a list of p2pool generated blocks can be easily found by looking at the donation address amounts = dict( (script, (subsidy - 1) * (199 * weight) // (200 * total_weight)) for (script, weight) in weights.iteritems()) amounts[new_script] = amounts.get(new_script, 0) + (subsidy - 1) // 200 amounts[SCRIPT] = amounts.get( SCRIPT, 0) + (subsidy - 1) * (199 * donation_weight) // (200 * total_weight) amounts[SCRIPT] = amounts.get(SCRIPT, 0) + subsidy - sum( amounts.itervalues()) # collect any extra satoshis :P if sum(amounts.itervalues()) != subsidy: raise ValueError() if any(x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted(amounts.iterkeys(), key=lambda script: (amounts[script], script)) dests = dests[-4000:] # block length limit, unlikely to ever be hit share_info = dict( share_data=share_data, target=target, timestamp=math.clip(desired_timestamp, (previous_share.timestamp - 60, previous_share.timestamp + 60)) if previous_share is not None else desired_timestamp, ) return share_info, dict( version=1, tx_ins=[ dict( previous_output=None, sequence=None, script=share_data['coinbase'].ljust(2, '\x00'), ) ], tx_outs=[ dict(value=0, script='\x20' + bitcoin_data.HashType().pack( share_info_type.hash256(share_info))) ] + [ dict(value=amounts[script], script=script) for script in dests if amounts[script] ], lock_time=0, )