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)
def check(self, tracker, other_txs=None): from p2pool import p2p counts = None if self.share_data['previous_share_hash'] is not None: previous_share = tracker.items[self.share_data['previous_share_hash']] if tracker.get_height(self.share_data['previous_share_hash']) >= self.net.CHAIN_LENGTH: counts = get_desired_version_counts(tracker, tracker.get_nth_parent_hash(previous_share.hash, self.net.CHAIN_LENGTH*9//10), self.net.CHAIN_LENGTH//10) if type(self) is type(previous_share): pass elif type(self) is type(previous_share).SUCCESSOR: # switch only valid if 60% of hashes in [self.net.CHAIN_LENGTH*9//10, self.net.CHAIN_LENGTH] for new version if counts.get(self.VERSION, 0) < sum(counts.itervalues())*60//100: raise p2p.PeerMisbehavingError('switch without enough hash power upgraded') else: raise p2p.PeerMisbehavingError('''%s can't follow %s''' % (type(self).__name__, type(previous_share).__name__)) elif type(self) is type(previous_share).SUCCESSOR: raise p2p.PeerMisbehavingError('switch without enough history') other_tx_hashes = [tracker.items[tracker.get_nth_parent_hash(self.hash, share_count)].share_info['new_transaction_hashes'][tx_count] for share_count, tx_count in self.iter_transaction_hash_refs()] if other_txs is not None and not isinstance(other_txs, dict): other_txs = dict((bitcoin_data.hash256(bitcoin_data.tx_type.pack(tx)), tx) for tx in other_txs) share_info, gentx, other_tx_hashes2, get_share = self.generate_transaction(tracker, self.share_info['share_data'], self.header['bits'].target, self.share_info['timestamp'], self.share_info['bits'].target, self.contents['ref_merkle_link'], [(h, None) for h in other_tx_hashes], self.net, known_txs=other_txs, last_txout_nonce=self.contents['last_txout_nonce'], segwit_data=self.share_info.get('segwit_data', None)) assert other_tx_hashes2 == other_tx_hashes if share_info != self.share_info: raise ValueError('share_info invalid') if bitcoin_data.get_txid(gentx) != self.gentx_hash: raise ValueError('''gentx doesn't match hash_link''') if bitcoin_data.calculate_merkle_link([None] + other_tx_hashes, 0) != self.merkle_link: # the other hash commitments are checked in the share_info assertion raise ValueError('merkle_link and other_tx_hashes do not match') update_min_protocol_version(counts, self) return gentx # only used by as_block
def load_share(share, net, peer): if share['type'] in [0, 1, 2, 3]: from p2pool import p2p raise p2p.PeerMisbehavingError('sent an obsolete share') elif share['type'] == 4: return Share(net, peer, other_txs=None, **Share.share1a_type.unpack(share['contents'])) elif share['type'] == 5: share1b = Share.share1b_type.unpack(share['contents']) return Share(net, peer, merkle_link=bitcoin_data.calculate_merkle_link([0] + [bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in share1b['other_txs']], 0), **share1b) elif share['type'] == 6: return NewShare(net, peer, other_txs=None, **NewShare.share1a_type.unpack(share['contents'])) elif share['type'] == 7: share1b = NewShare.share1b_type.unpack(share['contents']) return NewShare(net, peer, merkle_link=bitcoin_data.calculate_merkle_link([0] + [bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in share1b['other_txs']], 0), **share1b) else: raise ValueError('unknown share type: %r' % (share['type'],))
def check(self, tracker): from p2pool import p2p if self.share_data['previous_share_hash'] is not None: previous_share = tracker.items[self.share_data['previous_share_hash']] if type(self) is type(previous_share): pass elif type(self) is type(previous_share).SUCCESSOR: if tracker.get_height(previous_share.hash) < self.net.CHAIN_LENGTH: from p2pool import p2p raise p2p.PeerMisbehavingError('switch without enough history') # switch only valid if 85% of hashes in [self.net.CHAIN_LENGTH*9//10, self.net.CHAIN_LENGTH] for new version counts = get_desired_version_counts(tracker, tracker.get_nth_parent_hash(previous_share.hash, self.net.CHAIN_LENGTH*9//10), self.net.CHAIN_LENGTH//10) if counts.get(self.VERSION, 0) < sum(counts.itervalues())*85//100: raise p2p.PeerMisbehavingError('switch without enough hash power upgraded') else: raise p2p.PeerMisbehavingError('''%s can't follow %s''' % (type(self).__name__, type(previous_share).__name__)) other_tx_hashes = [tracker.items[tracker.get_nth_parent_hash(self.hash, share_count)].share_info['new_transaction_hashes'][tx_count] for share_count, tx_count in self.iter_transaction_hash_refs()] share_info, gentx, other_tx_hashes2, get_share = self.generate_transaction(tracker, self.share_info['share_data'], self.header['bits'].target, self.share_info['timestamp'], self.share_info['bits'].target, self.contents['ref_merkle_link'], [(h, None) for h in other_tx_hashes], self.net, last_txout_nonce=self.contents['last_txout_nonce']) assert other_tx_hashes2 == other_tx_hashes if share_info != self.share_info: raise ValueError('share_info invalid') if bitcoin_data.hash256(bitcoin_data.tx_type.pack(gentx)) != self.gentx_hash: raise ValueError('''gentx doesn't match hash_link''') if bitcoin_data.calculate_merkle_link([None] + other_tx_hashes, 0) != self.merkle_link: raise ValueError('merkle_link and other_tx_hashes do not match') return gentx # only used by as_block
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), ))
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)
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-4], cls.gentx_before_refhash), # for FRC Parent added -(tx_version in set([2]) and 4 or 0) merkle_link=bitcoin_data.calculate_merkle_link([None] + other_transaction_hashes, 0), )) assert share.header == header # checks merkle_root return share
def __init__(self, net, peer, common, merkle_link, other_txs): self.net = net self.peer = peer self.common = common self.min_header = common['min_header'] self.share_info = common['share_info'] self.hash_link = common['hash_link'] self.merkle_link = merkle_link self.other_txs = other_txs if len(self.share_info['share_data']['coinbase']) > 100: raise ValueError('''coinbase too large! %i bytes''' % (len(self.self.share_data['coinbase']),)) if len(merkle_link['branch']) > 16: raise ValueError('merkle branch too long!') if p2pool.DEBUG and other_txs is not None and bitcoin_data.calculate_merkle_link([0] + [bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in other_txs], 0) != merkle_link: raise ValueError('merkle_link and other_txs do not match') assert not self.hash_link['extra_data'], repr(self.hash_link['extra_data']) self.share_data = self.share_info['share_data'] self.max_target = self.share_info['max_bits'].target self.target = self.share_info['bits'].target self.timestamp = self.share_info['timestamp'] self.previous_hash = self.share_data['previous_share_hash'] self.new_script = bitcoin_data.pubkey_hash_to_script2(self.share_data['pubkey_hash']) self.desired_version = self.share_data['desired_version'] if self.timestamp < net.SWITCH_TIME: from p2pool import p2p raise p2p.PeerMisbehavingError('peer sent a new-style share with a timestamp before the switch time') self.gentx_hash = check_hash_link( self.hash_link, self.get_ref_hash(net, self.share_info, common['ref_merkle_link']) + pack.IntType(32).pack(0), self.gentx_before_refhash, ) merkle_root = bitcoin_data.check_merkle_link(self.gentx_hash, merkle_link) self.header = dict(self.min_header, merkle_root=merkle_root) self.pow_hash = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(self.header)) self.hash = self.header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(self.header)) if self.pow_hash > self.target: raise p2p.PeerMisbehavingError('share PoW invalid') if other_txs is not None and not self.pow_hash <= self.header['bits'].target: raise ValueError('other_txs provided when not a block solution') if other_txs is None and self.pow_hash <= self.header['bits'].target: raise ValueError('other_txs not provided when a block solution') # XXX eww self.time_seen = time.time()
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
def load_share(share, net, peer): if share['type'] in [0, 1, 2, 3]: from p2pool import p2p raise p2p.PeerMisbehavingError('sent an obsolete share') elif share['type'] == 4: return Share(net, peer, other_txs=None, **Share.share1a_type.unpack(share['contents'])) elif share['type'] == 5: share1b = Share.share1b_type.unpack(share['contents']) return Share(net, peer, merkle_link=bitcoin_data.calculate_merkle_link([0] + [ bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in share1b['other_txs'] ], 0), **share1b) else: raise ValueError('unknown share type: %r' % (share['type'], ))
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 - 4], cls.gentx_before_refhash ), # for FRC Parent added -(tx_version in set([2]) and 4 or 0) merkle_link=bitcoin_data.calculate_merkle_link( [None] + other_transaction_hashes, 0), )) assert share.header == header # checks merkle_root return share
def __init__(self, net, peer, common, merkle_link, other_txs): self.net = net self.peer = peer self.common = common self.min_header = common['min_header'] self.share_info = common['share_info'] self.hash_link = common['hash_link'] self.merkle_link = merkle_link self.other_txs = other_txs if len(self.share_info['share_data']['coinbase']) > 100: raise ValueError( '''coinbase too large! %i bytes''' % (len(self.share_info['share_data']['coinbase']), )) if len(merkle_link['branch']) > 16: raise ValueError('merkle branch too long!') if p2pool.DEBUG and other_txs is not None and bitcoin_data.calculate_merkle_link( [0] + [ bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in other_txs ], 0) != merkle_link: raise ValueError('merkle_link and other_txs do not match') assert not self.hash_link['extra_data'], repr( self.hash_link['extra_data']) self.share_data = self.share_info['share_data'] self.max_target = self.share_info['max_bits'].target self.target = self.share_info['bits'].target self.timestamp = self.share_info['timestamp'] self.previous_hash = self.share_data['previous_share_hash'] self.new_script = bitcoin_data.pubkey_hash_to_script2( self.share_data['pubkey_hash']) self.desired_version = self.share_data['desired_version'] self.gentx_hash = check_hash_link( self.hash_link, self.get_ref_hash(net, self.share_info, common['ref_merkle_link']) + pack.IntType(32).pack(0), self.gentx_before_refhash, ) merkle_root = bitcoin_data.check_merkle_link(self.gentx_hash, merkle_link) self.header = dict(self.min_header, merkle_root=merkle_root) self.pow_hash = net.PARENT.POW_FUNC( bitcoin_data.block_header_type.pack(self.header)) self.hash = self.header_hash = bitcoin_data.hash256( bitcoin_data.block_header_type.pack(self.header)) if self.pow_hash > self.target: from p2pool import p2p raise p2p.PeerMisbehavingError('share PoW invalid') if other_txs is not None and not self.pow_hash <= self.header[ 'bits'].target: raise ValueError('other_txs provided when not a block solution') if other_txs is None and self.pow_hash <= self.header['bits'].target: raise ValueError('other_txs not provided when a block solution') # XXX eww self.time_seen = time.time()
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
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): t0 = time.time() 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 # including witnesses all_transaction_stripped_size = 0 # stripped size all_transaction_real_size = 0 # including witnesses, for statistics new_transaction_weight = 0 all_transaction_weight = 0 transaction_hash_refs = [] other_transaction_hashes = [] t1 = time.time() 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 t2 = time.time() for tx_hash, fee in desired_other_transaction_hashes_and_fees: if known_txs is not None: this_stripped_size = bitcoin_data.tx_id_type.packed_size(known_txs[tx_hash]) this_real_size = bitcoin_data.tx_type.packed_size(known_txs[tx_hash]) this_weight = this_real_size + 3*this_stripped_size else: # we're just verifying someone else's share. We'll calculate sizes in should_punish_reason() this_stripped_size = 0 this_real_size = 0 this_weight = 0 if all_transaction_stripped_size + this_stripped_size + 80 + cls.gentx_size + 500 > net.BLOCK_MAX_SIZE: break if all_transaction_weight + this_weight + 4*80 + cls.gentx_weight + 2000 > net.BLOCK_MAX_WEIGHT: break if tx_hash in tx_hash_to_this: this = tx_hash_to_this[tx_hash] if known_txs is not None: all_transaction_stripped_size += this_stripped_size all_transaction_real_size += this_real_size all_transaction_weight += this_weight else: if known_txs is not None: new_transaction_size += this_real_size all_transaction_stripped_size += this_stripped_size all_transaction_real_size += this_real_size new_transaction_weight += this_weight all_transaction_weight += this_weight new_transaction_hashes.append(tx_hash) this = [0, len(new_transaction_hashes)-1] transaction_hash_refs.extend(this) other_transaction_hashes.append(tx_hash) t3 = time.time() if transaction_hash_refs and max(transaction_hash_refs) < 2**16: transaction_hash_refs = array.array('H', transaction_hash_refs) elif transaction_hash_refs and max(transaction_hash_refs) < 2**32: # in case we see blocks with more than 65536 tx transaction_hash_refs = array.array('L', transaction_hash_refs) t4 = time.time() if all_transaction_stripped_size: print "Generating a share with %i bytes, %i WU (new: %i B, %i WU) in %i tx (%i new), plus est gentx of %i bytes/%i WU" % ( all_transaction_real_size, all_transaction_weight, new_transaction_size, new_transaction_weight, len(other_transaction_hashes), len(new_transaction_hashes), cls.gentx_size, cls.gentx_weight) print "Total block stripped size=%i B, full size=%i B, weight: %i WU" % ( 80+all_transaction_stripped_size+cls.gentx_size, 80+all_transaction_real_size+cls.gentx_size, 3*80+all_transaction_weight+cls.gentx_weight) 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 ) if cls.VERSION < 32 else max(desired_timestamp, (previous_share.timestamp + 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 previous_share != None and desired_timestamp > previous_share.timestamp + 180: print "Warning: Previous share's timestamp is %i seconds old." % int(desired_timestamp - previous_share.timestamp) print "Make sure your system clock is accurate, and ensure that you're connected to decent peers." print "If your clock is more than 300 seconds behind, it can result in orphaned shares." print "(It's also possible that this share is just taking a long time to mine.)" if previous_share != None and previous_share.timestamp > int(time.time()) + 3: print "WARNING! Previous share's timestamp is %i seconds in the future. This is not normal." % \ int(previous_share.timestamp - (int(time.time()))) print "Make sure your system clock is accurate. Errors beyond 300 sec result in orphaned shares." 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 t5 = time.time() if p2pool.BENCH: print "%8.3f ms for data.py:generate_transaction(). Parts: %8.3f %8.3f %8.3f %8.3f %8.3f " % ( (t5-t0)*1000., (t1-t0)*1000., (t2-t1)*1000., (t3-t2)*1000., (t4-t3)*1000., (t5-t4)*1000.) 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