Просмотр исходного кода

make sure that objects are definitely hashed to unique values

Malte Kraus 8 лет назад
Родитель
Сommit
b22e599a10
2 измененных файлов с 15 добавлено и 7 удалено
  1. 8 5
      src/block.py
  2. 7 2
      src/transaction.py

+ 8 - 5
src/block.py

@@ -2,6 +2,7 @@
 
 
 from datetime import datetime
 from datetime import datetime
 from binascii import hexlify, unhexlify
 from binascii import hexlify, unhexlify
+from struct import pack
 import json
 import json
 import logging
 import logging
 import math
 import math
@@ -92,10 +93,14 @@ class Block:
         """ Verify that the merkle root hash is correct for the transactions in this block. """
         """ Verify that the merkle root hash is correct for the transactions in this block. """
         return merkle_tree(self.transactions).get_hash() == self.merkle_root_hash
         return merkle_tree(self.transactions).get_hash() == self.merkle_root_hash
 
 
-    def _int_to_bytes(self, val: int):
+    @staticmethod
+    def _int_to_bytes(val: int) -> bytes:
+        """ Turns an (arbitrarily long) integer into a bytes sequence. """
         l = val.bit_length()
         l = val.bit_length()
-        l = (0 if l % 8 == 0 or l == 0 else 1) + l // 8;
-        return val.to_bytes(l, 'little')
+        l = (0 if l % 8 == 0 or l == 0 else 1) + l // 8
+        # we need to include the length in the hash in some way, otherwise e.g.
+        # the numbers (0xffff, 0x00) would be encoded identically to (0xff, 0xff00)
+        return pack("<Q", l) + val.to_bytes(l, 'little')
 
 
     def get_partial_hash(self):
     def get_partial_hash(self):
         """
         """
@@ -103,8 +108,6 @@ class Block:
         work can use this partial hash to efficiently try different nonces. Other uses should
         work can use this partial hash to efficiently try different nonces. Other uses should
         use :any:`get_hash` to get the complete hash.
         use :any:`get_hash` to get the complete hash.
         """
         """
-        # TODO: the dynamically sized values here could in theory allow two different objects
-        # to hash to the same value (e.g. ('ab', 'c') has same hash as ('a', 'bc') )
         hasher = get_hasher()
         hasher = get_hasher()
         hasher.update(self.prev_block_hash)
         hasher.update(self.prev_block_hash)
         hasher.update(self.merkle_root_hash)
         hasher.update(self.merkle_root_hash)

+ 7 - 2
src/transaction.py

@@ -102,12 +102,17 @@ class Transaction:
             h = get_hasher()
             h = get_hasher()
             if self.iv is not None:
             if self.iv is not None:
                 h.update(self.iv)
                 h.update(self.iv)
+
+            h.update(Block._int_to_bytes(len(self.targets)))
             for target in self.targets:
             for target in self.targets:
-                h.update(str(target.amount).encode())
+                h.update(Block._int_to_bytes(target.amount))
                 h.update(target.recipient_pk.as_bytes())
                 h.update(target.recipient_pk.as_bytes())
+
+            h.update(Block._int_to_bytes(len(self.inputs)))
             for inp in self.inputs:
             for inp in self.inputs:
                 h.update(inp.transaction_hash)
                 h.update(inp.transaction_hash)
-                h.update(str(inp.output_idx).encode())
+                h.update(Block._int_to_bytes(inp.output_idx))
+
             self._hash = h.digest()
             self._hash = h.digest()
         return self._hash
         return self._hash