Don't include transaction if it still too young
[p2pool.git] / p2pool / data.py
index 43e0d7c..72afbe4 100644 (file)
@@ -49,7 +49,7 @@ def load_share(share, net, peer_addr):
     else:
         raise ValueError('unknown share type: %r' % (share['type'],))
 
-DONATION_SCRIPT = '4104ffd03de44a6e11b9917f3a29f9443283d9871c9d743ef30d5eddcd37094b64d1b3d8090496b53256786bf5c82932ec23c3b74d9f05a6f95a8b5529352656664bac'.decode('hex')
+DONATION_SCRIPT  = '41043b253cc0b5c8ce26f24b84bb955bec955cbb4643f19ab7ea073884f22874abdafc42040b97efec3c9eeb29ce69022a96cc1772f8bc805f78af0d3dc5c441db5fac'.decode('hex')
 
 class Share(object):
     VERSION = 13
@@ -117,7 +117,7 @@ class Share(object):
             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.NEW_SHARE_PERIOD*attempts_per_second) - 1 if attempts_per_second else 2**256-1
+            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)
@@ -139,6 +139,8 @@ class Share(object):
                 this = tx_hash_to_this[tx_hash]
             else:
                 if known_txs is not None:
+                    if known_txs[tx_hash]['timestamp'] > desired_timestamp - 30:
+                        continue
                     this_size = bitcoin_data.tx_type.packed_size(known_txs[tx_hash])
                     if new_transaction_size + this_size > 50000: # only allow 50 kB of new txns/share
                         break
@@ -152,14 +154,14 @@ class Share(object):
         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))
+            share_data = dict(share_data, subsidy=share_data['subsidy'])
         else:
             assert base_subsidy is not None
-            share_data = dict(share_data, subsidy=base_subsidy + definite_fees)
+            share_data = dict(share_data, subsidy=base_subsidy)
         
         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.NEW_SPREAD*bitcoin_data.target_to_average_attempts(block_target),
+            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)
         
@@ -179,8 +181,8 @@ class Share(object):
             max_bits=max_bits,
             bits=bits,
             timestamp=math.clip(desired_timestamp, (
-                (previous_share.timestamp + net.NEW_SHARE_PERIOD) - (net.NEW_SHARE_PERIOD - 1), # = previous_share.timestamp + 1
-                (previous_share.timestamp + net.NEW_SHARE_PERIOD) + (net.NEW_SHARE_PERIOD - 1),
+                (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,
@@ -190,6 +192,9 @@ class Share(object):
         
         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,
@@ -268,8 +273,7 @@ class Share(object):
         )
         merkle_root = bitcoin_data.check_merkle_link(self.gentx_hash, self.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))
+        self.pow_hash = self.hash = self.header_hash = bitcoin_data.scrypt(bitcoin_data.block_header_type.pack(self.header))
         
         if self.target > net.MAX_TARGET:
             from p2pool import p2p
@@ -316,6 +320,7 @@ class Share(object):
         
         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
+# workaround
         if share_info != self.share_info:
             raise ValueError('share_info invalid')
         if bitcoin_data.hash256(bitcoin_data.tx_type.pack(gentx)) != self.gentx_hash:
@@ -369,7 +374,7 @@ class Share(object):
         other_txs = self._get_other_txs(tracker, known_txs)
         if other_txs is None:
             return None # not all txs present
-        return dict(header=self.header, txs=[self.check(tracker)] + other_txs)
+        return dict(header=self.header, txs=[self.check(tracker)] + other_txs, signature='')
 
 
 class WeightsSkipList(forest.TrackerSkipList):
@@ -430,7 +435,7 @@ class OkayTracker(forest.Tracker):
         try:
             share.check(self)
         except:
-            log.err(None, 'Share check failed:')
+            log.err(None, 'Share check failed: %064x -> %064x' % (share.hash, share.previous_hash if share.previous_hash is not None else 0))
             return False
         else:
             self.verified.add(share)
@@ -438,21 +443,21 @@ class OkayTracker(forest.Tracker):
     
     def think(self, block_rel_height_func, previous_block, bits, known_txs):
         desired = set()
+        bad_peer_addresses = set()
         
         # O(len(self.heads))
         #   make 'unverified heads' set?
         # for each overall head, attempt verification
         # if it fails, attempt on parent, and repeat
         # if no successful verification because of lack of parents, request parent
-        bads = set()
+        bads = []
         for head in set(self.heads) - set(self.verified.heads):
             head_height, last = self.get_height_and_last(head)
             
             for share in self.get_chain(head, head_height if last is None else min(5, max(0, head_height - self.net.CHAIN_LENGTH))):
                 if self.attempt_verify(share):
                     break
-                if share.hash in self.heads:
-                    bads.add(share.hash)
+                bads.append(share.hash)
             else:
                 if last is not None:
                     desired.add((
@@ -463,10 +468,16 @@ class OkayTracker(forest.Tracker):
                     ))
         for bad in bads:
             assert bad not in self.verified.items
-            assert bad in self.heads
+            #assert bad in self.heads
+            bad_share = self.items[bad]
+            if bad_share.peer_addr is not None:
+                bad_peer_addresses.add(bad_share.peer_addr)
             if p2pool.DEBUG:
                 print "BAD", bad
-            self.remove(bad)
+            try:
+                self.remove(bad)
+            except NotImplementedError:
+                pass
         
         # try to get at least CHAIN_LENGTH height for each verified head, requesting parents if needed
         for head in list(self.verified.heads):
@@ -527,7 +538,7 @@ class OkayTracker(forest.Tracker):
             for peer_addr, hash, ts, targ in desired:
                 print '   ', None if peer_addr is None else '%s:%i' % peer_addr, format_hash(hash), math.format_dt(time.time() - ts), bitcoin_data.target_to_difficulty(targ), ts >= timestamp_cutoff, targ <= target_cutoff
         
-        return best, [(peer_addr, hash) for peer_addr, hash, ts, targ in desired if ts >= timestamp_cutoff], decorated_heads
+        return best, [(peer_addr, hash) for peer_addr, hash, ts, targ in desired if ts >= timestamp_cutoff], decorated_heads, bad_peer_addresses
     
     def score(self, share_hash, block_rel_height_func):
         # returns approximate lower bound on chain's hashrate in the last self.net.CHAIN_LENGTH*15//16*self.net.SHARE_PERIOD time
@@ -594,7 +605,7 @@ def get_desired_version_counts(tracker, best_share_hash, dist):
         res[share.desired_version] = res.get(share.desired_version, 0) + bitcoin_data.target_to_average_attempts(share.target)
     return res
 
-def get_warnings(tracker, best_share, net, bitcoind_warning, bitcoind_work_value):
+def get_warnings(tracker, best_share, net, bitcoind_getinfo, bitcoind_work_value):
     res = []
     
     desired_version_counts = get_desired_version_counts(tracker, best_share,
@@ -605,9 +616,13 @@ def get_warnings(tracker, best_share, net, bitcoind_warning, bitcoind_work_value
             'An upgrade is likely necessary. Check http://p2pool.forre.st/ for more information.' % (
                 majority_desired_version, 100*desired_version_counts[majority_desired_version]/sum(desired_version_counts.itervalues())))
     
-    if bitcoind_warning is not None:
-        if 'This is a pre-release test build' not in bitcoind_warning:
-            res.append('(from bitcoind) %s' % (bitcoind_warning,))
+    if bitcoind_getinfo['errors'] != '':
+        if 'This is a pre-release test build' not in bitcoind_getinfo['errors']:
+            res.append('(from bitcoind) %s' % (bitcoind_getinfo['errors'],))
+    
+    version_warning = getattr(net, 'VERSION_WARNING', lambda v: None)(bitcoind_getinfo['version'])
+    if version_warning is not None:
+        res.append(version_warning)
     
     if time.time() > bitcoind_work_value['last_update'] + 60:
         res.append('''LOST CONTACT WITH BITCOIND for %s! Check that it isn't frozen or dead!''' % (math.format_dt(time.time() - bitcoind_work_value['last_update']),))