Beispiel #1
0
 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)
Beispiel #2
0
def get_stale_counts(tracker, share_hash, lookbehind, rates=False):
    res = {}
    for share in tracker.get_chain(share_hash, lookbehind - 1):
        res['good'] = res.get('good', 0) + bitcoin_data.target_to_average_attempts(share.target)
        s = share.share_data['stale_info']
        if s is not None:
            res[s] = res.get(s, 0) + bitcoin_data.target_to_average_attempts(share.target)
    if rates:
        dt = tracker.items[share_hash].timestamp - tracker.items[tracker.get_nth_parent_hash(share_hash, lookbehind - 1)].timestamp
        res = dict((k, v/dt) for k, v in res.iteritems())
    return res
Beispiel #3
0
 def __init__(self, net, my_share_hashes, my_doa_share_hashes):
     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.Tracker(delta_type=forest.get_attributedelta_type(dict(forest.AttributeDelta.attrs,
         work=lambda share: bitcoin_data.target_to_average_attempts(share.target),
         my_count=lambda share: 1 if share.hash in my_share_hashes else 0,
         my_doa_count=lambda share: 1 if share.hash in my_doa_share_hashes else 0,
         my_orphan_announce_count=lambda share: 1 if share.hash in my_share_hashes and share.share_data['stale_info'] == 253 else 0,
         my_dead_announce_count=lambda share: 1 if share.hash in my_share_hashes and share.share_data['stale_info'] == 254 else 0,
     )), subset_of=self)
     self.get_cumulative_weights = WeightsSkipList(self)
Beispiel #4
0
def get_desired_version_counts(tracker, best_share_hash, dist):
    res = {}
    for share in tracker.get_chain(best_share_hash, dist):
        res[share.desired_version] = res.get(share.desired_version, 0) + bitcoin_data.target_to_average_attempts(
            share.target
        )
    return res
Beispiel #5
0
 def get_delta(self, element):
     from p2pool.bitcoin import data as bitcoin_data
     if element is None:
         return (2**256, {}, 0, 0) # XXX
     share = self.tracker.shares[element]
     att = bitcoin_data.target_to_average_attempts(share.target)
     return 1, {share.new_script: att*(65535-share.donation)}, att*65535, att*share.donation
Beispiel #6
0
 def from_element(cls, share):
     return cls(share.hash,
         1, bitcoin_data.target_to_average_attempts(share.target),
         1 if share.hash in cls.my_share_hashes else 0,
         1 if share.hash in cls.my_doa_share_hashes else 0,
         1 if share.hash in cls.my_share_hashes and share.share_data['stale_info'] == 253 else 0,
         1 if share.hash in cls.my_share_hashes and share.share_data['stale_info'] == 254 else 0,
     share.previous_hash)
Beispiel #7
0
 def get_height_work_and_last(self, share_hash):
     height = 0
     work = 0
     while share_hash in self.shares:
         share_hash, work_inc = self.shares[share_hash].previous_hash, bitcoin_data.target_to_average_attempts(self.shares[share_hash].target)
         height += 1
         work += work_inc
     return height, work, share_hash
Beispiel #8
0
def get_expected_payouts(tracker, best_share_hash, block_target, subsidy, net):
    weights, total_weight, donation_weight = tracker.get_cumulative_weights(
        best_share_hash,
        min(tracker.get_height(best_share_hash), net.REAL_CHAIN_LENGTH),
        65535 * net.SPREAD * bitcoin_data.target_to_average_attempts(block_target),
    )
    res = dict((script, subsidy * weight // total_weight) for script, weight in weights.iteritems())
    res[DONATION_SCRIPT] = res.get(DONATION_SCRIPT, 0) + subsidy - sum(res.itervalues())
    return res
Beispiel #9
0
def get_pool_attempts_per_second(tracker, previous_share_hash, net, dist=None):
    if dist is None:
        dist = net.TARGET_LOOKBEHIND
    # XXX could be optimized to use nth_parent and cumulative_weights
    chain = list(itertools.islice(tracker.get_chain_to_root(previous_share_hash), dist))
    attempts = sum(bitcoin_data.target_to_average_attempts(share.target) for share in chain[:-1])
    time = chain[0].timestamp - chain[-1].timestamp
    if time == 0:
        time = 1
    return attempts//time
Beispiel #10
0
 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,
     )
Beispiel #11
0
    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"],
        )
Beispiel #12
0
def get_expected_payouts(tracker, best_share_hash, block_target, subsidy, net):
    weights, total_weight, donation_weight = tracker.get_cumulative_weights(
        best_share_hash,
        min(tracker.get_height(best_share_hash),
            net.REAL_CHAIN_LENGTH), 65535 * net.SPREAD *
        bitcoin_data.target_to_average_attempts(block_target))
    res = dict((script, subsidy * weight // total_weight)
               for script, weight in weights.iteritems())
    res[DONATION_SCRIPT] = res.get(DONATION_SCRIPT, 0) + subsidy - sum(
        res.itervalues())
    return res
Beispiel #13
0
 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)
Beispiel #14
0
 def get_delta(element):
     from p2pool.bitcoin import data as bitcoin_data
     share = self.tracker.items[element]
     att = bitcoin_data.target_to_average_attempts(share.target)
     #(share_count2, weights2, total_weight2, total_donation_weight2)
     # share_count2 = 1,
     # weights2 = {share.new_script: att*(65535-share.share_data['donation'])},
     # total_weight2 = att*65535,
     # total_donation_weight2 = att*share.share_data['donation']
     return 1, {
         share.new_script: att * (65535 - share.share_data['donation'])
     }, att * 65535, att * share.share_data['donation']
Beispiel #15
0
 def score(self, share_hash, ht):
     head_height, last = self.verified.get_height_and_last(share_hash)
     score2 = 0
     attempts = 0
     max_height = 0
     # XXX should only look past a certain share, not at recent ones
     share2_hash = self.verified.get_nth_parent_hash(share_hash, min(self.net.CHAIN_LENGTH//2, head_height//2)) if last is not None else share_hash
     # XXX this must go in the opposite direction for max_height to make sense
     for share in reversed(list(itertools.islice(self.verified.get_chain_known(share2_hash), self.net.CHAIN_LENGTH))):
         max_height = max(max_height, ht.get_min_height(share.header['previous_block']))
         attempts += bitcoin_data.target_to_average_attempts(share.target)
         this_score = attempts//(ht.get_highest_height() - max_height + 1)
         if this_score > score2:
             score2 = this_score
     return min(head_height, self.net.CHAIN_LENGTH), score2
Beispiel #16
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
Beispiel #17
0
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,
    )
Beispiel #18
0
        def __sub__(self, other):
            if self.head == other.head:
                return self.__class__(other.tail, self.tail, **dict((k, getattr(self, k) - getattr(other, k)) for k in attrs))
            elif self.tail == other.tail:
                return self.__class__(self.head, other.head, **dict((k, getattr(self, k) - getattr(other, k)) for k in attrs))
            else:
                raise AssertionError()
        
        def __repr__(self):
            return '%s(%r, %r%s)' % (self.__class__, self.head, self.tail, ''.join(', %s=%r' % (k, getattr(self, k)) for k in attrs))
    ProtoAttributeDelta.attrs = attrs
    return ProtoAttributeDelta

AttributeDelta = get_attributedelta_type(dict(
    height=lambda share: 1,
    work=lambda share: bitcoin_data.target_to_average_attempts(share.target),
))

class Tracker(object):
    def __init__(self, shares=[], delta_type=AttributeDelta):
        self.shares = {} # hash -> share
        self.reverse_shares = {} # delta.tail -> set of share_hashes
        
        self.heads = {} # head hash -> tail_hash
        self.tails = {} # tail hash -> set of head hashes
        
        self.deltas = {} # share_hash -> delta, ref
        self.reverse_deltas = {} # ref -> set of share_hashes
        
        self.ref_generator = itertools.count()
        self.delta_refs = {} # ref -> delta
Beispiel #19
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, 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
Beispiel #20
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
Beispiel #21
0
    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
Beispiel #22
0
 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']
Beispiel #23
0
 def remove(self, share_hash):
     assert isinstance(share_hash, (int, long, type(None)))
     if share_hash not in self.shares:
         raise KeyError()
     
     share = self.shares[share_hash]
     del share_hash
     
     children = self.reverse_shares.get(share.hash, set())
     
     # move height refs referencing children down to this, so they can be moved up in one step
     if share.previous_hash in self.reverse_height_refs:
         if share.previous_hash not in self.tails:
             for x in list(self.reverse_heights.get(self.reverse_height_refs.get(share.previous_hash, object()), set())):
                 self.get_last(x)
         for x in list(self.reverse_heights.get(self.reverse_height_refs.get(share.hash, object()), set())):
             self.get_last(x)
         assert share.hash not in self.reverse_height_refs, list(self.reverse_heights.get(self.reverse_height_refs.get(share.hash, None), set()))
     
     if share.hash in self.heads and share.previous_hash in self.tails:
         tail = self.heads.pop(share.hash)
         self.tails[tail].remove(share.hash)
         if not self.tails[share.previous_hash]:
             self.tails.pop(share.previous_hash)
     elif share.hash in self.heads:
         tail = self.heads.pop(share.hash)
         self.tails[tail].remove(share.hash)
         if self.reverse_shares[share.previous_hash] != set([share.hash]):
             pass # has sibling
         else:
             self.tails[tail].add(share.previous_hash)
             self.heads[share.previous_hash] = tail
     elif share.previous_hash in self.tails and len(self.reverse_shares[share.previous_hash]) <= 1:
         heads = self.tails.pop(share.previous_hash)
         for head in heads:
             self.heads[head] = share.hash
         self.tails[share.hash] = set(heads)
         
         # move ref pointing to this up
         if share.previous_hash in self.reverse_height_refs:
             assert share.hash not in self.reverse_height_refs, list(self.reverse_heights.get(self.reverse_height_refs.get(share.hash, object()), set()))
             
             ref = self.reverse_height_refs[share.previous_hash]
             cur_height, cur_hash, cur_work = self.height_refs[ref]
             assert cur_hash == share.previous_hash
             self.height_refs[ref] = cur_height - 1, share.hash, cur_work - bitcoin_data.target_to_average_attempts(share.target)
             del self.reverse_height_refs[share.previous_hash]
             self.reverse_height_refs[share.hash] = ref
     else:
         raise NotImplementedError()
     
     # delete height entry, and ref if it is empty
     if share.hash in self.heights:
         _, ref, _ = self.heights.pop(share.hash)
         self.reverse_heights[ref].remove(share.hash)
         if not self.reverse_heights[ref]:
             del self.reverse_heights[ref]
             _, ref_hash, _ = self.height_refs.pop(ref)
             del self.reverse_height_refs[ref_hash]
     
     self.shares.pop(share.hash)
     self.reverse_shares[share.previous_hash].remove(share.hash)
     if not self.reverse_shares[share.previous_hash]:
         self.reverse_shares.pop(share.previous_hash)
     
     self.removed.happened(share)
Beispiel #24
0
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,
    )
Beispiel #25
0
 def from_element(cls, share):
     return cls(share.hash, 1, bitcoin_data.target_to_average_attempts(share.target), share.previous_hash)
Beispiel #26
0
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,
    )
Beispiel #27
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
Beispiel #28
0
    def remove(self, share_hash):
        assert isinstance(share_hash, (int, long, type(None)))
        if share_hash not in self.shares:
            raise KeyError()

        share = self.shares[share_hash]
        del share_hash

        children = self.reverse_shares.get(share.hash, set())

        # move height refs referencing children down to this, so they can be moved up in one step
        if share.previous_hash in self.reverse_height_refs:
            if share.previous_hash not in self.tails:
                for x in list(
                        self.reverse_heights.get(
                            self.reverse_height_refs.get(
                                share.previous_hash, object()), set())):
                    self.get_last(x)
            for x in list(
                    self.reverse_heights.get(
                        self.reverse_height_refs.get(share.hash, object()),
                        set())):
                self.get_last(x)
            assert share.hash not in self.reverse_height_refs, list(
                self.reverse_heights.get(
                    self.reverse_height_refs.get(share.hash, None), set()))

        if share.hash in self.heads and share.previous_hash in self.tails:
            tail = self.heads.pop(share.hash)
            self.tails[tail].remove(share.hash)
            if not self.tails[share.previous_hash]:
                self.tails.pop(share.previous_hash)
        elif share.hash in self.heads:
            tail = self.heads.pop(share.hash)
            self.tails[tail].remove(share.hash)
            if self.reverse_shares[share.previous_hash] != set([share.hash]):
                pass  # has sibling
            else:
                self.tails[tail].add(share.previous_hash)
                self.heads[share.previous_hash] = tail
        elif share.previous_hash in self.tails and len(
                self.reverse_shares[share.previous_hash]) <= 1:
            heads = self.tails.pop(share.previous_hash)
            for head in heads:
                self.heads[head] = share.hash
            self.tails[share.hash] = set(heads)

            # move ref pointing to this up
            if share.previous_hash in self.reverse_height_refs:
                assert share.hash not in self.reverse_height_refs, list(
                    self.reverse_heights.get(
                        self.reverse_height_refs.get(share.hash, object()),
                        set()))

                ref = self.reverse_height_refs[share.previous_hash]
                cur_height, cur_hash, cur_work = self.height_refs[ref]
                assert cur_hash == share.previous_hash
                self.height_refs[
                    ref] = cur_height - 1, share.hash, cur_work - bitcoin_data.target_to_average_attempts(
                        share.target)
                del self.reverse_height_refs[share.previous_hash]
                self.reverse_height_refs[share.hash] = ref
        else:
            raise NotImplementedError()

        # delete height entry, and ref if it is empty
        if share.hash in self.heights:
            _, ref, _ = self.heights.pop(share.hash)
            self.reverse_heights[ref].remove(share.hash)
            if not self.reverse_heights[ref]:
                del self.reverse_heights[ref]
                _, ref_hash, _ = self.height_refs.pop(ref)
                del self.reverse_height_refs[ref_hash]

        self.shares.pop(share.hash)
        self.reverse_shares[share.previous_hash].remove(share.hash)
        if not self.reverse_shares[share.previous_hash]:
            self.reverse_shares.pop(share.previous_hash)

        self.removed.happened(share)
Beispiel #29
0
def get_desired_version_counts(tracker, best_share_hash, dist):
    res = {}
    for share in tracker.get_chain(best_share_hash, dist):
        res[share.desired_version] = res.get(share.desired_version, 0) + bitcoin_data.target_to_average_attempts(share.target)
    return res
Beispiel #30
0
 def _get_height_jump(self, share_hash):
     if share_hash in self.heights:
         height_to1, ref, work_inc1 = self.heights[share_hash]
         height_to2, share_hash, work_inc2 = self.height_refs[ref]
         height_inc = height_to1 + height_to2
         work_inc = work_inc1 + work_inc2
     else:
         height_inc, share_hash, work_inc = 1, self.shares[share_hash].previous_hash, bitcoin_data.target_to_average_attempts(self.shares[share_hash].target)
     return height_inc, share_hash, work_inc