Jelajahi Sumber

dynamically recompute the desired block difficulty

Malte Kraus 8 tahun lalu
induk
melakukan
94e1c2d083
4 mengubah file dengan 33 tambahan dan 10 penghapusan
  1. 0 1
      src/block.py
  2. 24 3
      src/blockchain.py
  3. 2 3
      src/chainbuilder.py
  4. 7 3
      src/proof_of_work.py

+ 0 - 1
src/block.py

@@ -162,7 +162,6 @@ class Block:
             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

+ 24 - 3
src/blockchain.py

@@ -2,6 +2,8 @@
 
 __all__ = ['Blockchain']
 import logging
+from datetime import timedelta
+from fractions import Fraction
 from typing import List, Dict, Optional
 
 class Blockchain:
@@ -98,9 +100,28 @@ class Blockchain:
 
     def compute_difficulty(self, prev_block: 'Block'=None) -> int:
         """ Compute the desired difficulty for the block after `prev_block` (defaults to `head`). """
-        # TODO: dynamic calculation
-        # TODO: verify difficulty in new blocks
-        return self.head.difficulty
+        BLOCK_INTERVAL = 120
+        BLOCK_TARGET_TIMEDELTA = Fraction(int(timedelta(minutes=1).total_seconds() * 1000 * 1000))
+
+        if prev_block is None:
+            prev_block = self.head
+
+        block_idx = self.block_indices[prev_block.hash] + 1
+        if block_idx % BLOCK_INTERVAL != 0:
+            return prev_block.difficulty
+
+        duration = prev_block.time - self.blocks[block_idx - BLOCK_INTERVAL].time
+        duration = Fraction(int(duration.total_seconds() * 1000 * 1000))
+
+        prev_difficulty = Fraction(prev_block.difficulty)
+        hash_rate = prev_difficulty * BLOCK_INTERVAL / duration
+
+        new_difficulty = hash_rate * BLOCK_TARGET_TIMEDELTA / BLOCK_INTERVAL
+
+        if new_difficulty < self.blocks[0].difficulty:
+            new_difficulty = self.blocks[0].difficulty
+
+        return int(new_difficulty)
 
     def compute_blockreward(self, prev_block: 'Block') -> int:
         """ Compute the block reward that is expected for the block following `prev_block`. """

+ 2 - 3
src/chainbuilder.py

@@ -73,7 +73,7 @@ class ChainBuilder:
 
     def _new_primary_block_chain(self, chain: 'Blockchain'):
         """ Does all the housekeeping that needs to be done when a new longest chain is found. """
-        logging.info("new primary block chain with height %d", len(chain.blocks))
+        logging.info("new primary block chain with height %d with current difficulty %d", len(chain.blocks), chain.head.difficulty)
         self._assert_thread_safety()
         self.primary_block_chain = chain
         todelete = set()
@@ -101,8 +101,7 @@ class ChainBuilder:
 
         if unc[-1].height == 0:
             chain = Blockchain(unc[::-1])
-            times_ok = all(b.verify_time(chain) for b in unc)
-            if times_ok and chain.verify_all_transactions():
+            if chain.verify_all():
                 self._new_primary_block_chain(chain)
             self.unconfirmed_block_chain = []
         else:

+ 7 - 3
src/proof_of_work.py

@@ -8,10 +8,14 @@ __all__ = ['verify_proof_of_work', 'GENESIS_DIFFICULTY', 'ProofOfWork']
 
 def verify_proof_of_work(block: 'Block'):
     """ Verify the proof of work on a block. """
-    return int.from_bytes(block.hash, byteorder='little', signed=False) > block.difficulty
+    return int.from_bytes(block.hash, byteorder='little', signed=False) > (MAX_HASH - MAX_HASH // block.difficulty)
 
-GENESIS_DIFFICULTY = MAX_HASH - (MAX_HASH // 10000)
-""" The difficulty of the genesis block. """
+GENESIS_DIFFICULTY = 1000
+"""
+The difficulty of the genesis block.
+
+Right now this is the average required number of hashes to compute one valid block.
+"""
 
 class ProofOfWork:
     """