def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, net): previous_share = tracker.shares[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None height, last = tracker.get_height_and_last(share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second(tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 if attempts_per_second else 2**256-1 pre_target2 = math.clip(pre_target, (previous_share.max_target*9//10, previous_share.max_target*11//10)) pre_target3 = math.clip(pre_target2, (0, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//10, pre_target3))) weights, total_weight, donation_weight = tracker.get_cumulative_weights(share_data['previous_share_hash'], min(height, net.REAL_CHAIN_LENGTH), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target), ) assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) amounts = dict((script, share_data['subsidy']*(199*weight)//(200*total_weight)) for script, weight in weights.iteritems()) # 99.5% goes according to weights prior to this share this_script = bitcoin_data.pubkey_hash_to_script2(share_data['pubkey_hash']) amounts[this_script] = amounts.get(this_script, 0) + share_data['subsidy']//200 # 0.5% goes to block finder amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + share_data['subsidy'] - sum(amounts.itervalues()) # all that's left over is the donation weight and some extra satoshis due to rounding if sum(amounts.itervalues()) != share_data['subsidy'] or any(x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hit share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=math.clip(desired_timestamp, ( (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), )) 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=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] + [dict( value=0, script='\x20' + cls.get_ref_hash(net, share_info, ref_merkle_link), )], lock_time=0, )
def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes_and_fees, net, known_txs=None, last_txout_nonce=0, base_subsidy=None): previous_share = tracker.items[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None height, last = tracker.get_height_and_last(share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second(tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 if attempts_per_second else 2**256-1 pre_target2 = math.clip(pre_target, (previous_share.max_target*9//10, previous_share.max_target*11//10)) pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//30, pre_target3))) new_transaction_hashes = [] new_transaction_size = 0 transaction_hash_refs = [] other_transaction_hashes = [] past_shares = list(tracker.get_chain(share_data['previous_share_hash'], min(height, 100))) tx_hash_to_this = {} for i, share in enumerate(past_shares): for j, tx_hash in enumerate(share.new_transaction_hashes): if tx_hash not in tx_hash_to_this: tx_hash_to_this[tx_hash] = [1+i, j] # share_count, tx_count for tx_hash, fee in desired_other_transaction_hashes_and_fees: if tx_hash in tx_hash_to_this: this = tx_hash_to_this[tx_hash] else: if known_txs is not None: this_size = bitcoin_data.tx_type.packed_size(known_txs[tx_hash]) if new_transaction_size + this_size > 50000: # only allow 50 kB of new txns/share break new_transaction_size += this_size new_transaction_hashes.append(tx_hash) this = [0, len(new_transaction_hashes)-1] transaction_hash_refs.extend(this) other_transaction_hashes.append(tx_hash) included_transactions = set(other_transaction_hashes) removed_fees = [fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash not in included_transactions] definite_fees = sum(0 if fee is None else fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash in included_transactions) if None not in removed_fees: share_data = dict(share_data, subsidy=share_data['subsidy'] - sum(removed_fees)) else: assert base_subsidy is not None share_data = dict(share_data, subsidy=base_subsidy + definite_fees) weights, total_weight, donation_weight = tracker.get_cumulative_weights(previous_share.share_data['previous_share_hash'] if previous_share is not None else None, min(height, net.REAL_CHAIN_LENGTH-1), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target), ) assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) amounts = dict((script, share_data['subsidy']*(199*weight)//(200*total_weight)) for script, weight in weights.iteritems()) # 99.5% goes according to weights prior to this share this_script = bitcoin_data.pubkey_hash_to_script2(share_data['pubkey_hash']) amounts[this_script] = amounts.get(this_script, 0) + share_data['subsidy']//200 # 0.5% goes to block finder amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + share_data['subsidy'] - sum(amounts.itervalues()) # all that's left over is the donation weight and some extra satoshis due to rounding if sum(amounts.itervalues()) != share_data['subsidy'] or any(x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hit share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=math.clip(desired_timestamp, ( (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), )) if previous_share is not None else desired_timestamp, new_transaction_hashes=new_transaction_hashes, transaction_hash_refs=transaction_hash_refs, absheight=((previous_share.absheight if previous_share is not None else 0) + 1) % 2**32, abswork=((previous_share.abswork if previous_share is not None else 0) + bitcoin_data.target_to_average_attempts(bits.target)) % 2**128, ) gentx = dict( version=1, tx_ins=[dict( previous_output=None, sequence=None, script=share_data['coinbase'], )], tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] + [dict( value=0, script='\x6a\x28' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(64).pack(last_txout_nonce), )], lock_time=0, ) def get_share(header, last_txout_nonce=last_txout_nonce): min_header = dict(header); del min_header['merkle_root'] share = cls(net, None, dict( min_header=min_header, share_info=share_info, ref_merkle_link=dict(branch=[], index=0), last_txout_nonce=last_txout_nonce, hash_link=prefix_to_hash_link(bitcoin_data.tx_type.pack(gentx)[:-32-8-4], cls.gentx_before_refhash), merkle_link=bitcoin_data.calculate_merkle_link([None] + other_transaction_hashes, 0), )) assert share.header == header # checks merkle_root return share return share_info, gentx, other_transaction_hashes, get_share
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 generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes_and_fees, net, known_txs=None, last_txout_nonce=0, base_subsidy=None): previous_share = tracker.items[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None def get_coinbase_fee(share_data, outpointsnum): # calculate neccessary coinbase fee # coinbase usually seems like this: # # 01000000 - nVersion # 1a184351 - nTimestamp # 01 - Inputs num # 0000000000000000000000000000000000000000000000000000000000000000 - Input hash # ffffffff - Input index (-1) # 0a02732a062f503253482f - Scriptsig # ffffffff - nSequence # 15 - Outpoints num # (User outpoints, 44 bytes per each) # (Donation outpoint, 76 bytes) # P2Pool service outpoint (contains merkle link), 46 bytes # # 1027000000000000 # 25 # 2417cc2063b11fd5255c7e5605780de78163ffc698ed22856bff1a5d880c3c44e400000000 # Giving users some time to upgrade coinbase_size = 50 + (1 + len(share_data['coinbase'])) + outpointsnum * 44 + 76 + 46 # if coinbase size is greater than 1000 bytes, it should pay fee (0.01 per 1000 bytes) if coinbase_size > 1000: return int(ceil(coinbase_size / 1000.0) * minout) return 0 if base_subsidy is None: base_subsidy = net.PARENT.SUBSIDY_FUNC(block_target) # current user payout script this_script = share_data['script'] height, last = tracker.get_height_and_last(share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second(tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 if attempts_per_second else 2**256-1 pre_target2 = math.clip(pre_target, (previous_share.max_target*9//10, previous_share.max_target*11//10)) pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//10, pre_target3))) if p2pool.DEBUG: print print "Share Info Bits Target (DATA)" print bitcoin_data.target_to_difficulty(bits.target) print new_transaction_hashes = [] new_transaction_size = 0 transaction_hash_refs = [] other_transaction_hashes = [] past_shares = list(tracker.get_chain(share_data['previous_share_hash'], min(height, 100))) tx_hash_to_this = {} for i, share in enumerate(past_shares): for j, tx_hash in enumerate(share.new_transaction_hashes): if tx_hash not in tx_hash_to_this: tx_hash_to_this[tx_hash] = [1+i, j] # share_count, tx_count for tx_hash, fee in desired_other_transaction_hashes_and_fees: if tx_hash in tx_hash_to_this: this = tx_hash_to_this[tx_hash] else: if known_txs is not None: this_size = bitcoin_data.tx_type.packed_size(known_txs[tx_hash]) if new_transaction_size + this_size > 50000: # only allow 50 kB of new txns/share break new_transaction_size += this_size new_transaction_hashes.append(tx_hash) this = [0, len(new_transaction_hashes)-1] transaction_hash_refs.extend(this) other_transaction_hashes.append(tx_hash) included_transactions = set(other_transaction_hashes) share_data = dict(share_data, subsidy=base_subsidy) raw_weights, total_weight, donation_weight = tracker.get_cumulative_weights(share_data['previous_share_hash'], min(height, net.REAL_CHAIN_LENGTH), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target), ) # calculate "raw" subsidy raw_subsidy = share_data['subsidy'] - 4 * minout - get_coinbase_fee(share_data, len(raw_weights) + 1) # calculate "raw" amounts raw_amounts = dict((script, raw_subsidy*weight//total_weight) for script, weight in raw_weights.iteritems()) total_remowed_weight = 0 weights = {} # iterate list and collect all weights, which produces less than 0.01 payout # it's neccessary due to NVC/PPC protocol-level limitations for coinbase outpoint size for x in raw_amounts.keys(): if raw_amounts[x] < minout and x not in [this_script, DONATION_SCRIPT]: total_remowed_weight = total_remowed_weight + raw_weights[x] else: weights[x] = raw_weights[x] total_weight = total_weight - total_remowed_weight assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) # base subsidy value calculated as: # [subsidy - (0.01 for donation + 0.01 for current user + 0.01 for p2pool outpoint) - netfee] my_subsidy = share_data['subsidy'] - 3 * minout - get_coinbase_fee(share_data, len(weights) + 1) # subsidy goes according to weights prior to this share amounts = dict((script, my_subsidy*weight//total_weight) for script, weight in weights.iteritems()) # all that's left over is the donation weight and some extra satoshis due to rounding amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + my_subsidy - sum(amounts.itervalues()) if sum(amounts.itervalues()) != my_subsidy or any(x < 0 for x in amounts.itervalues()): raise ValueError() # add 0.01 coin to donation, to satisfy the protocol amounts[DONATION_SCRIPT] = amounts[DONATION_SCRIPT] + minout # add 0.01 to current user output, to satisfy the protocol amounts[this_script] = amounts.get(this_script, 0) + minout dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hi share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=desired_timestamp, # need better solution # timestamp=math.clip(desired_timestamp, ( # (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 # (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), # )) if previous_share is not None else desired_timestamp, new_transaction_hashes=new_transaction_hashes, transaction_hash_refs=transaction_hash_refs, ) if p2pool.DEBUG: print print "Desired timestamp (DATA)" print desired_timestamp print time.time() print print "Prev Share timestamp (DATA)" print previous_share.timestamp print time.time() print print "Share info timestamp (DATA)" print share_info['timestamp'] print time.time() print gentx = dict( version=1, # coinbase timestamp must be older than share/block timestamp # maybe there are more elegant solution, but this hack works quite well for now timestamp=share_info['timestamp'], tx_ins=[dict( previous_output=None, sequence=None, script=share_data['coinbase'], )], tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] + [dict( # add 0.01 coin to service output, to satisfy the protocol value=minout, script='\x24' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(32).pack(last_txout_nonce), )], lock_time=0, ) def get_share(header, last_txout_nonce=last_txout_nonce): min_header = dict(header); del min_header['merkle_root'] share = cls(net, None, dict( min_header=min_header, share_info=share_info, ref_merkle_link=dict(branch=[], index=0), last_txout_nonce=last_txout_nonce, hash_link=prefix_to_hash_link(bitcoin_data.tx_type.pack(gentx)[:-32-4-4], cls.gentx_before_refhash), merkle_link=bitcoin_data.calculate_merkle_link([None] + other_transaction_hashes, 0), )) assert share.header == header # checks merkle_root return share return share_info, gentx, other_transaction_hashes, get_share
def _shift(x, shift, pad_item): left_pad = math2.clip(shift, (0, len(x))) right_pad = math2.clip(-shift, (0, len(x))) return [pad_item]*left_pad + x[right_pad:-left_pad if left_pad else None] + [pad_item]*right_pad
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, )
def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes_and_fees, net, known_txs=None, last_txout_nonce=0, base_subsidy=None): previous_share = tracker.items[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None height, last = tracker.get_height_and_last(share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second(tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 if attempts_per_second else 2**256-1 pre_target2 = math.clip(pre_target, (previous_share.max_target*9//10, previous_share.max_target*11//10)) pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//30, pre_target3))) new_transaction_hashes = [] new_transaction_size = 0 transaction_hash_refs = [] other_transaction_hashes = [] past_shares = list(tracker.get_chain(share_data['previous_share_hash'], min(height, 100))) tx_hash_to_this = {} for i, share in enumerate(past_shares): for j, tx_hash in enumerate(share.new_transaction_hashes): if tx_hash not in tx_hash_to_this: tx_hash_to_this[tx_hash] = [1+i, j] # share_count, tx_count for tx_hash, fee in desired_other_transaction_hashes_and_fees: if tx_hash in tx_hash_to_this: this = tx_hash_to_this[tx_hash] else: if known_txs is not None: if known_txs[tx_hash]['timestamp'] > desired_timestamp - 30: continue this_size = bitcoin_data.tx_type.packed_size(known_txs[tx_hash]) if new_transaction_size + this_size > 50000: # only allow 50 kB of new txns/share break new_transaction_size += this_size new_transaction_hashes.append(tx_hash) this = [0, len(new_transaction_hashes)-1] transaction_hash_refs.extend(this) other_transaction_hashes.append(tx_hash) included_transactions = set(other_transaction_hashes) removed_fees = [fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash not in included_transactions] definite_fees = sum(0 if fee is None else fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash in included_transactions) if None not in removed_fees: share_data = dict(share_data, subsidy=share_data['subsidy'] - sum(removed_fees)) else: assert base_subsidy is not None share_data = dict(share_data, subsidy=base_subsidy + definite_fees) weights, total_weight, donation_weight = tracker.get_cumulative_weights(previous_share.share_data['previous_share_hash'] if previous_share is not None else None, max(0, min(height, net.REAL_CHAIN_LENGTH) - 1), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target), ) assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) worker_payout = share_data['subsidy'] amounts = dict((script, worker_payout*(199*weight)//(200*total_weight)) for script, weight in weights.iteritems()) # 99.5% goes according to weights prior to this share this_script = bitcoin_data.pubkey_hash_to_script2(share_data['pubkey_hash']) amounts[this_script] = amounts.get(this_script, 0) + worker_payout//200 # 0.5% goes to block finder amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + worker_payout - sum(amounts.itervalues()) # all that's left over is the donation weight and some extra satoshis due to rounding if sum(amounts.itervalues()) != worker_payout or any(x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hit worker_tx = [dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=math.clip(desired_timestamp, ( (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), )) if previous_share is not None else desired_timestamp, new_transaction_hashes=new_transaction_hashes, transaction_hash_refs=transaction_hash_refs, absheight=((previous_share.absheight if previous_share is not None else 0) + 1) % 2**32, abswork=((previous_share.abswork if previous_share is not None else 0) + bitcoin_data.target_to_average_attempts(bits.target)) % 2**128, ) gentx = dict( version=1, # coinbase timestamp must be older than share/block timestamp # maybe there are more elegant solution, but this hack works quite well for now timestamp=share_info['timestamp'], tx_ins=[dict( previous_output=None, sequence=None, script=share_data['coinbase'], )], tx_outs=worker_tx + [dict( value=0, script='\x6a\x28' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(64).pack(last_txout_nonce), )], lock_time=0, ) def get_share(header, last_txout_nonce=last_txout_nonce): min_header = dict(header); del min_header['merkle_root'] share = cls(net, None, dict( min_header=min_header, share_info=share_info, ref_merkle_link=dict(branch=[], index=0), last_txout_nonce=last_txout_nonce, hash_link=prefix_to_hash_link(bitcoin_data.tx_type.pack(gentx)[:-32-8-4], cls.gentx_before_refhash), merkle_link=bitcoin_data.calculate_merkle_link([None] + other_transaction_hashes, 0), )) assert share.header == header # checks merkle_root return share return share_info, gentx, other_transaction_hashes, get_share
def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, other_transaction_hashes, net): previous_share = tracker.items[ share_data['previous_share_hash']] if share_data[ 'previous_share_hash'] is not None else None height, last = tracker.get_height_and_last( share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second( tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256 // ( net.SHARE_PERIOD * attempts_per_second) - 1 if attempts_per_second else 2**256 - 1 pre_target2 = math.clip(pre_target, (previous_share.max_target * 9 // 10, previous_share.max_target * 11 // 10)) pre_target3 = math.clip(pre_target2, (0, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound( pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound( math.clip(desired_target, (pre_target3 // 10, pre_target3))) weights, total_weight, donation_weight = tracker.get_cumulative_weights( share_data['previous_share_hash'], min(height, net.REAL_CHAIN_LENGTH), 65535 * net.SPREAD * bitcoin_data.target_to_average_attempts(block_target), ) assert total_weight == sum(weights.itervalues()) + donation_weight, ( total_weight, sum(weights.itervalues()) + donation_weight) amounts = dict( (script, share_data['subsidy'] * (199 * weight) // (200 * total_weight)) for script, weight in weights.iteritems( )) # 99.5% goes according to weights prior to this share this_script = bitcoin_data.pubkey_hash_to_script2( share_data['pubkey_hash']) amounts[this_script] = amounts.get( this_script, 0) + share_data['subsidy'] // 200 # 0.5% goes to block finder amounts[DONATION_SCRIPT] = amounts.get( DONATION_SCRIPT, 0 ) + share_data['subsidy'] - sum( amounts.itervalues() ) # all that's left over is the donation weight and some extra satoshis due to rounding if sum(amounts.itervalues()) != share_data['subsidy'] or any( x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted( amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[ -4000:] # block length limit, unlikely to ever be hit share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=math.clip( desired_timestamp, ( (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), )) if previous_share is not None else desired_timestamp, ) gentx = dict( version=1, tx_ins=[ dict( previous_output=None, sequence=None, script=share_data['coinbase'].ljust(2, '\x00'), ) ], tx_outs=[ dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT ] + [ dict( value=0, script='\x20' + cls.get_ref_hash(net, share_info, ref_merkle_link), ) ], lock_time=0, ) def get_share(header, transactions): assert transactions[0] == gentx and [ bitcoin_data.hash256(bitcoin_data.tx_type.pack(tx)) for tx in transactions[1:] ] == other_transaction_hashes min_header = dict(header) del min_header['merkle_root'] hash_link = prefix_to_hash_link( bitcoin_data.tx_type.pack(gentx)[:-32 - 4], cls.gentx_before_refhash) merkle_link = bitcoin_data.calculate_merkle_link( [None] + other_transaction_hashes, 0) pow_hash = net.PARENT.POW_FUNC( bitcoin_data.block_header_type.pack(header)) return cls(net, None, dict( min_header=min_header, share_info=share_info, hash_link=hash_link, ref_merkle_link=dict(branch=[], index=0), ), merkle_link=merkle_link, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None) return share_info, gentx, other_transaction_hashes, get_share
def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes, net, known_txs=None, last_txout_nonce=0): previous_share = tracker.items[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None height, last = tracker.get_height_and_last(share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second(tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1 if attempts_per_second else 2**256-1 pre_target2 = math.clip(pre_target, (previous_share.max_target*9//10, previous_share.max_target*11//10)) pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//10, pre_target3))) weights, total_weight, donation_weight = tracker.get_cumulative_weights(share_data['previous_share_hash'], min(height, net.REAL_CHAIN_LENGTH), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target), ) assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) amounts = dict((script, share_data['subsidy']*(199*weight)//(200*total_weight)) for script, weight in weights.iteritems()) # 99.5% goes according to weights prior to this share this_script = bitcoin_data.pubkey_hash_to_script2(share_data['pubkey_hash']) amounts[this_script] = amounts.get(this_script, 0) + share_data['subsidy']//200 # 0.5% goes to block finder amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + share_data['subsidy'] - sum(amounts.itervalues()) # all that's left over is the donation weight and some extra satoshis due to rounding if sum(amounts.itervalues()) != share_data['subsidy'] or any(x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hit new_transaction_hashes = [] new_transaction_size = 0 transaction_hash_refs = [] other_transaction_hashes = [] for tx_hash in desired_other_transaction_hashes: for i, share in enumerate(tracker.get_chain(share_data['previous_share_hash'], min(height, 100))): if tx_hash in share.new_transaction_hashes: this = dict(share_count=i+1, tx_count=share.new_transaction_hashes.index(tx_hash)) break else: if known_txs is not None: this_size = bitcoin_data.tx_type.packed_size(known_txs[tx_hash]) if new_transaction_size + this_size > 50000: # only allow 50 kB of new txns/share break new_transaction_size += this_size new_transaction_hashes.append(tx_hash) this = dict(share_count=0, tx_count=len(new_transaction_hashes)-1) transaction_hash_refs.append(this) other_transaction_hashes.append(tx_hash) share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=math.clip(desired_timestamp, ( (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), )) if previous_share is not None else desired_timestamp, new_transaction_hashes=new_transaction_hashes, transaction_hash_refs=transaction_hash_refs, ) gentx = dict( version=1, tx_ins=[dict( previous_output=None, sequence=None, script=share_data['coinbase'], )], tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] + [dict( value=0, script='\x24' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(32).pack(last_txout_nonce), )], lock_time=0, ) def get_share(header, transactions): min_header=dict(header);del min_header['merkle_root'] return cls(net, None, dict( min_header=min_header, share_info=share_info, ref_merkle_link=dict(branch=[], index=0), last_txout_nonce=last_txout_nonce, hash_link=prefix_to_hash_link(bitcoin_data.tx_type.pack(gentx)[:-32-4-4], cls.gentx_before_refhash), merkle_link=bitcoin_data.calculate_merkle_link([None] + other_transaction_hashes, 0), )) return share_info, gentx, other_transaction_hashes, get_share
def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes_and_fees, net, known_txs=None, last_txout_nonce=0, base_subsidy=None, segwit_data=None): previous_share = tracker.items[ share_data['previous_share_hash']] if share_data[ 'previous_share_hash'] is not None else None height, last = tracker.get_height_and_last( share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: pre_target3 = net.MAX_TARGET else: attempts_per_second = get_pool_attempts_per_second( tracker, share_data['previous_share_hash'], net.TARGET_LOOKBEHIND, min_work=True, integer=True) pre_target = 2**256 // ( net.SHARE_PERIOD * attempts_per_second) - 1 if attempts_per_second else 2**256 - 1 pre_target2 = math.clip(pre_target, (previous_share.max_target * 9 // 10, previous_share.max_target * 11 // 10)) pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound( pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound( math.clip(desired_target, (pre_target3 // 30, pre_target3))) new_transaction_hashes = [] new_transaction_size = 0 transaction_hash_refs = [] other_transaction_hashes = [] past_shares = list( tracker.get_chain(share_data['previous_share_hash'], min(height, 100))) tx_hash_to_this = {} for i, share in enumerate(past_shares): for j, tx_hash in enumerate(share.new_transaction_hashes): if tx_hash not in tx_hash_to_this: tx_hash_to_this[tx_hash] = [1 + i, j] # share_count, tx_count for tx_hash, fee in desired_other_transaction_hashes_and_fees: if tx_hash in tx_hash_to_this: this = tx_hash_to_this[tx_hash] else: if known_txs is not None: this_size = bitcoin_data.tx_type.packed_size( known_txs[tx_hash]) if new_transaction_size + this_size > cls.MAX_NEW_TXS_SIZE: # limit the size of new txns/share break new_transaction_size += this_size new_transaction_hashes.append(tx_hash) this = [0, len(new_transaction_hashes) - 1] transaction_hash_refs.extend(this) other_transaction_hashes.append(tx_hash) included_transactions = set(other_transaction_hashes) removed_fees = [ fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash not in included_transactions ] definite_fees = sum( 0 if fee is None else fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash in included_transactions) if None not in removed_fees: share_data = dict(share_data, subsidy=share_data['subsidy'] - sum(removed_fees)) else: assert base_subsidy is not None share_data = dict(share_data, subsidy=base_subsidy + definite_fees) weights, total_weight, donation_weight = tracker.get_cumulative_weights( previous_share.share_data['previous_share_hash'] if previous_share is not None else None, max(0, min(height, net.REAL_CHAIN_LENGTH) - 1), 65535 * net.SPREAD * bitcoin_data.target_to_average_attempts(block_target), ) assert total_weight == sum(weights.itervalues()) + donation_weight, ( total_weight, sum(weights.itervalues()) + donation_weight) amounts = dict( (script, share_data['subsidy'] * (199 * weight) // (200 * total_weight)) for script, weight in weights.iteritems( )) # 99.5% goes according to weights prior to this share this_script = bitcoin_data.pubkey_hash_to_script2( share_data['pubkey_hash']) amounts[this_script] = amounts.get( this_script, 0) + share_data['subsidy'] // 200 # 0.5% goes to block finder amounts[DONATION_SCRIPT] = amounts.get( DONATION_SCRIPT, 0 ) + share_data['subsidy'] - sum( amounts.itervalues() ) # all that's left over is the donation weight and some extra satoshis due to rounding if sum(amounts.itervalues()) != share_data['subsidy'] or any( x < 0 for x in amounts.itervalues()): raise ValueError() dests = sorted( amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[ -4000:] # block length limit, unlikely to ever be hit segwit_activated = is_segwit_activated(cls.VERSION, net) if segwit_data is None and known_txs is None: segwit_activated = False if not (segwit_activated or known_txs is None) and any( bitcoin_data.is_segwit_tx(known_txs[h]) for h in other_transaction_hashes): raise ValueError('segwit transaction included before activation') if segwit_activated and known_txs is not None: share_txs = [(known_txs[h], bitcoin_data.get_txid(known_txs[h]), h) for h in other_transaction_hashes] segwit_data = dict( txid_merkle_link=bitcoin_data.calculate_merkle_link( [None] + [tx[1] for tx in share_txs], 0), wtxid_merkle_root=bitcoin_data.merkle_hash([0] + [ bitcoin_data.get_wtxid(tx[0], tx[1], tx[2]) for tx in share_txs ])) if segwit_activated and segwit_data is not None: witness_reserved_value_str = '[P2Pool]' * 4 witness_reserved_value = pack.IntType(256).unpack( witness_reserved_value_str) witness_commitment_hash = bitcoin_data.get_witness_commitment_hash( segwit_data['wtxid_merkle_root'], witness_reserved_value) share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), max_bits=max_bits, bits=bits, timestamp=math.clip( desired_timestamp, ( (previous_share.timestamp + net.SHARE_PERIOD) - (net.SHARE_PERIOD - 1), # = previous_share.timestamp + 1 (previous_share.timestamp + net.SHARE_PERIOD) + (net.SHARE_PERIOD - 1), )) if previous_share is not None else desired_timestamp, new_transaction_hashes=new_transaction_hashes, transaction_hash_refs=transaction_hash_refs, absheight=((previous_share.absheight if previous_share is not None else 0) + 1) % 2**32, abswork=( (previous_share.abswork if previous_share is not None else 0) + bitcoin_data.target_to_average_attempts(bits.target)) % 2**128, ) if segwit_activated: share_info['segwit_data'] = segwit_data gentx = dict( version=1, tx_ins=[ dict( previous_output=None, sequence=None, script=share_data['coinbase'], ) ], tx_outs=([ dict(value=0, script='\x6a\x24\xaa\x21\xa9\xed' + pack.IntType(256).pack(witness_commitment_hash)) ] if segwit_activated else []) + [ dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT ] + [ dict(value=0, script='\x6a\x28' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(64).pack(last_txout_nonce)) ], lock_time=0, ) if segwit_activated: gentx['marker'] = 0 gentx['flag'] = 1 gentx['witness'] = [[witness_reserved_value_str]] def get_share(header, last_txout_nonce=last_txout_nonce): min_header = dict(header) del min_header['merkle_root'] share = cls( net, None, dict( min_header=min_header, share_info=share_info, ref_merkle_link=dict(branch=[], index=0), last_txout_nonce=last_txout_nonce, hash_link=prefix_to_hash_link( bitcoin_data.tx_id_type.pack(gentx)[:-32 - 8 - 4], cls.gentx_before_refhash), merkle_link=bitcoin_data.calculate_merkle_link( [None] + other_transaction_hashes, 0), )) assert share.header == header # checks merkle_root return share return share_info, gentx, other_transaction_hashes, get_share