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

make sure that transactions in the same block do not conflict with each other

Malte Kraus 8 лет назад
Родитель
Сommit
e664b67c0c
5 измененных файлов с 32 добавлено и 12 удалено
  1. 9 1
      src/block.py
  2. 4 4
      src/chainbuilder.py
  3. 1 1
      src/mining.py
  4. 9 2
      src/mining_strategy.py
  5. 9 4
      src/transaction.py

+ 9 - 1
src/block.py

@@ -83,15 +83,23 @@ class Block:
 
     def verify_transactions(self, chain):
         """ Verify all transaction in this block are valid in the given block chain. """
+        if self.hash == GENESIS_BLOCK.hash:
+            return True
+
         mining_reward = None
         # TODO: mining fees and variable block rewards
+
+        prev_block = chain.get_block_by_hash(self.prev_block_hash)
+        assert prev_block is not None
+
+        trans_set = set(self.transactions)
         for t in self.transactions:
             if not t.inputs:
                 if mining_reward is not None:
                     return False
                 mining_reward = t
 
-            if not t.verify(chain):
+            if not t.verify(chain, trans_set - {t}, prev_block):
                 return False
         if mining_reward is not None:
             if sum(map(lambda t: t.amount, mining_reward.targets)) > chain.compute_blockreward(chain.get_block_by_hash(self.prev_block_hash)):

+ 4 - 4
src/chainbuilder.py

@@ -39,11 +39,11 @@ class ChainBuilder:
         Does all the housekeeping that needs to be done when a new longest chain is found.
         """
         self.primary_block_chain = chain
-        keys = set()
+        todelete = set()
         for (hash_val, trans) in self.unconfirmed_transactions.items():
-            if not trans.verify(chain):
-                keys.add(hash_val)
-        for hash_val in keys:
+            if not trans.verify(chain, set()):
+                todelete.add(hash_val)
+        for hash_val in todelete:
             del self.unconfirmed_transactions[hash_val]
 
         for handler in self.chain_change_handlers:

+ 1 - 1
src/mining.py

@@ -47,6 +47,6 @@ class Miner:
         primary chain changes.
         """
         chain = self.chainbuilder.primary_block_chain
-        transactions = [t for t in self.chainbuilder.unconfirmed_transactions if t.verify(chain)]
+        transactions = self.chainbuilder.unconfirmed_transactions
         block = mining_strategy.create_block(chain, transactions, self.reward_pubkey)
         self.start_mining(block)

+ 9 - 2
src/mining_strategy.py

@@ -14,11 +14,18 @@ def create_block(blockchain, unconfirmed_transactions, reward_pubkey):
     """
     head = blockchain.head
 
-    transactions = list(unconfirmed_transactions)
+    transactions = set()
+    for t in unconfirmed_transactions:
+        if t.verify(blockchain, transactions):
+            # TODO: choose most profitable of conflicting transactions
+            transactions.add(t)
+
+
     reward = blockchain.compute_blockreward(head)
     trans = Transaction([], [TransactionTarget(reward_pubkey, reward)], [], iv=head.hash)
-    transactions.append(trans)
+    transactions.add(trans)
 
+    transactions = list(transactions)
     tree = merkle_tree(transactions)
     difficulty = blockchain.compute_difficulty()
     return Block(None, head.hash, datetime.now(), 0, head.height + difficulty,

+ 9 - 4
src/transaction.py

@@ -108,15 +108,20 @@ class Transaction:
         return sender_pk.verify_sign(self.get_hash(), sig)
 
 
-    def _verify_single_spend(self, chain: Blockchain, prev_block: Block):
+    def _verify_single_spend(self, chain: Blockchain, other_trans: set, prev_block: Block):
         """ Verifies that all inputs have not been spent yet. """
-        if len(self.inputs) != len(set(self.inputs)):
+        inp_set = set(self.inputs)
+        if len(self.inputs) != len(inp_set):
             return False
+        other_inputs = {i for t in other_trans for i in t.inputs}
+        if other_inputs.intersection(inp_set):
+            return False
+
         for i in self.inputs:
             if not chain.is_coin_still_valid(i, prev_block):
                 return False
         return True
 
-    def verify(self, chain: Blockchain, prev_block:Block=None):
+    def verify(self, chain: Blockchain, other_trans:set, prev_block:Block=None):
         """ Verifies that this transaction is completely valid. """
-        return self._verify_single_spend(chain, prev_block) and self._verify_signatures(chain)
+        return self._verify_single_spend(chain, other_trans, prev_block) and self._verify_signatures(chain)