added merkle_link structure that combines branch and index
authorForrest Voight <forrest@forre.st>
Tue, 20 Mar 2012 17:36:25 +0000 (13:36 -0400)
committerForrest Voight <forrest@forre.st>
Tue, 20 Mar 2012 19:43:04 +0000 (15:43 -0400)
p2pool/bitcoin/data.py
p2pool/data.py
p2pool/main.py

index 481df58..628be33 100644 (file)
@@ -104,13 +104,15 @@ tx_type = pack.ComposedType([
     ('lock_time', pack.IntType(32)),
 ])
 
-merkle_branch_type = pack.ListType(pack.IntType(256))
+merkle_link_type = pack.ComposedType([
+    ('branch', pack.ListType(pack.IntType(256))),
+    ('index', pack.IntType(32)),
+])
 
 merkle_tx_type = pack.ComposedType([
     ('tx', tx_type),
     ('block_hash', pack.IntType(256)),
-    ('merkle_branch', merkle_branch_type),
-    ('index', pack.IntType(32)),
+    ('merkle_link', merkle_link_type),
 ])
 
 block_header_type = pack.ComposedType([
@@ -131,8 +133,7 @@ block_type = pack.ComposedType([
 
 aux_pow_type = pack.ComposedType([
     ('merkle_tx', merkle_tx_type),
-    ('merkle_branch', merkle_branch_type),
-    ('index', pack.IntType(32)),
+    ('merkle_link', merkle_link_type),
     ('parent_block_header', block_header_type),
 ])
 
@@ -168,11 +169,11 @@ def merkle_hash(hashes):
         return 0
     hash_list = list(hashes)
     while len(hash_list) > 1:
-        hash_list = [hash256(merkle_record_type.pack(dict(left=left, right=left if right is None else right)))
-            for left, right in zip(hash_list[::2], hash_list[1::2] + [None])]
+        hash_list = [hash256(merkle_record_type.pack(dict(left=left, right=right)))
+            for left, right in zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]])]
     return hash_list[0]
 
-def calculate_merkle_branch(hashes, index):
+def calculate_merkle_link(hashes, index):
     # XXX optimize this
     
     hash_list = [(h, i == index, []) for i, h in enumerate(hashes)]
@@ -195,13 +196,15 @@ def calculate_merkle_branch(hashes, index):
         assert check_merkle_branch(hashes[index], index, res) == hash_list[0][0]
     assert index == sum(k*2**i for i, k in enumerate([1-x['side'] for x in hash_list[0][2]]))
     
-    return res
+    return dict(branch=res, index=index)
 
-def check_merkle_branch(tip_hash, index, merkle_branch):
+def check_merkle_link(tip_hash, link):
+    if link['index'] >= 2**len(link['branch']):
+        raise ValueError('index too large')
     return reduce(lambda c, (i, h): hash256(merkle_record_type.pack(
-        dict(left=h, right=c) if 2**i & index else
+        dict(left=h, right=c) if (link['index'] >> i) & 1 else
         dict(left=c, right=h)
-    )), enumerate(merkle_branch), tip_hash)
+    )), enumerate(link['branch']), tip_hash)
 
 # targets
 
index a36fcd0..9eebd2b 100644 (file)
@@ -50,7 +50,7 @@ def load_share(share, net, peer):
         return Share(net, peer, other_txs=None, **Share.share1a_type.unpack(share['contents']))
     elif share['type'] == 3:
         share1b = Share.share1b_type.unpack(share['contents'])
-        return Share(net, peer, merkle_branch=bitcoin_data.calculate_merkle_branch([0] + [bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in share1b['other_txs']], 0), **share1b)
+        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'],))
 
@@ -86,7 +86,10 @@ class Share(object):
         ('min_header', small_block_header_type),
         ('share_info', share_info_type),
         ('hash_link', hash_link_type),
-        ('merkle_branch', bitcoin_data.merkle_branch_type),
+        ('merkle_link', pack.ComposedType([
+            ('branch', pack.ListType(pack.IntType(256))),
+            ('index', pack.IntType(0)), # it will always be 0
+        ])),
     ])
     
     share1b_type = pack.ComposedType([
@@ -163,17 +166,17 @@ class Share(object):
             lock_time=0,
         )
     
-    __slots__ = 'net min_header share_info hash_link merkle_branch other_txs hash share_data max_target target timestamp previous_hash new_script gentx_hash header pow_hash header_hash time_seen peer'.split(' ')
+    __slots__ = 'net min_header share_info hash_link merkle_link other_txs hash share_data max_target target timestamp previous_hash new_script gentx_hash header pow_hash header_hash time_seen peer'.split(' ')
     
-    def __init__(self, net, peer, min_header, share_info, hash_link, merkle_branch, other_txs):
+    def __init__(self, net, peer, min_header, share_info, hash_link, merkle_link, other_txs):
         if len(share_info['share_data']['coinbase']) > 100:
             raise ValueError('''coinbase too large! %i bytes''' % (len(self.share_data['coinbase']),))
         
-        if len(merkle_branch) > 16:
+        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_branch([0] + [bitcoin_data.hash256(bitcoin_data.tx_type.pack(x)) for x in other_txs], 0) != merkle_branch:
-            raise ValueError('merkle_branch and other_txs do not match')
+        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 hash_link['extra_data'], repr(hash_link['extra_data'])
         
@@ -182,7 +185,7 @@ class Share(object):
         self.min_header = min_header
         self.share_info = share_info
         self.hash_link = hash_link
-        self.merkle_branch = merkle_branch
+        self.merkle_link = merkle_link
         self.other_txs = other_txs
         
         self.share_data = self.share_info['share_data']
@@ -200,7 +203,7 @@ class Share(object):
             )))) + pack.IntType(32).pack(0),
             self.gentx_before_refhash,
         )
-        merkle_root = bitcoin_data.check_merkle_branch(self.gentx_hash, 0, merkle_branch)
+        merkle_root = bitcoin_data.check_merkle_link(self.gentx_hash, merkle_link)
         self.header = dict(min_header, merkle_root=merkle_root)
         self.pow_hash = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(self.header))
         self.header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(self.header))
@@ -230,7 +233,7 @@ class Share(object):
     
     def as_share(self):
         if not self.pow_hash <= self.header['bits'].target: # share1a
-            return dict(type=2, contents=self.share1a_type.pack(dict(min_header=self.min_header, share_info=self.share_info, hash_link=self.hash_link, merkle_branch=self.merkle_branch)))
+            return dict(type=2, contents=self.share1a_type.pack(dict(min_header=self.min_header, share_info=self.share_info, hash_link=self.hash_link, merkle_link=self.merkle_link)))
         else: # share1b
             return dict(type=3, contents=self.share1b_type.pack(dict(min_header=self.min_header, share_info=self.share_info, hash_link=self.hash_link, other_txs=self.other_txs)))
     
index 8d317d8..6f9948f 100644 (file)
@@ -38,7 +38,7 @@ def getwork(bitcoind):
         version=work['version'],
         previous_block_hash=int(work['previousblockhash'], 16),
         transactions=map(bitcoin_data.tx_type.unpack, packed_transactions),
-        merkle_branch=bitcoin_data.calculate_merkle_branch([0] + map(bitcoin_data.hash256, packed_transactions), 0),
+        merkle_link=bitcoin_data.calculate_merkle_link([0] + map(bitcoin_data.hash256, packed_transactions), 0), # using 0 is a bit of a hack, but will always work when index=0
         subsidy=work['coinbasevalue'],
         time=work['time'],
         bits=bitcoin_data.FloatingIntegerType().unpack(work['bits'].decode('hex')[::-1]) if isinstance(work['bits'], (str, unicode)) else bitcoin_data.FloatingInteger(work['bits']),
@@ -162,7 +162,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
             current_work2.set(dict(
                 time=work['time'],
                 transactions=work['transactions'],
-                merkle_branch=work['merkle_branch'],
+                merkle_link=work['merkle_link'],
                 subsidy=work['subsidy'],
                 clock_offset=time.time() - work['time'],
                 last_update=time.time(),
@@ -535,10 +535,10 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                 
                 transactions = [generate_tx] + list(current_work2.value['transactions'])
                 packed_generate_tx = bitcoin_data.tx_type.pack(generate_tx)
-                merkle_root = bitcoin_data.check_merkle_branch(bitcoin_data.hash256(packed_generate_tx), 0, current_work2.value['merkle_branch'])
+                merkle_root = bitcoin_data.check_merkle_link(bitcoin_data.hash256(packed_generate_tx), current_work2.value['merkle_link'])
                 
                 getwork_time = time.time()
-                merkle_branch = current_work2.value['merkle_branch']
+                merkle_link = current_work2.value['merkle_link']
                 
                 print 'New work for worker! Difficulty: %.06f Share difficulty: %.06f Total block value: %.6f %s including %i transactions' % (
                     bitcoin_data.target_to_difficulty(target),
@@ -585,11 +585,9 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                                         merkle_tx=dict(
                                             tx=transactions[0],
                                             block_hash=header_hash,
-                                            merkle_branch=merkle_branch,
-                                            index=0,
+                                            merkle_link=merkle_link,
                                         ),
-                                        merkle_branch=bitcoin_data.calculate_merkle_branch(hashes, index),
-                                        index=index,
+                                        merkle_link=bitcoin_data.calculate_merkle_link(hashes, index),
                                         parent_block_header=header,
                                     )).encode('hex'),
                                 )
@@ -608,7 +606,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint):
                     if pow_hash <= share_info['bits'].target:
                         min_header = dict(header);del min_header['merkle_root']
                         hash_link = p2pool_data.prefix_to_hash_link(packed_generate_tx[:-32-4], p2pool_data.Share.gentx_before_refhash)
-                        share = p2pool_data.Share(net, None, min_header, share_info, hash_link=hash_link, merkle_branch=merkle_branch, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
+                        share = p2pool_data.Share(net, None, min_header, share_info, hash_link=hash_link, merkle_link=merkle_link, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
                         
                         print 'GOT SHARE! %s %s prev %s age %.2fs%s' % (
                             request.getUser(),