blockchain.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. """ Definition of block chains. """
  2. __all__ = ['Blockchain', 'GENESIS_BLOCK']
  3. import logging
  4. from binascii import hexlify
  5. from collections import namedtuple
  6. from typing import Optional
  7. from datetime import datetime
  8. from .merkle import merkle_tree
  9. from .block import Block
  10. from .config import *
  11. from .utils import compute_blockreward_next_block
  12. # If you want to add transactions to the genesis block you can create a Transaction object and include it in the list below (after GENESIS_TARGET)
  13. GENESIS_BLOCK = Block("None; {} {}".format(DIFFICULTY_BLOCK_INTERVAL, DIFFICULTY_TIMEDELTA).encode(),
  14. datetime(2017, 3, 3, 10, 35, 26, 922898), 0, 0, datetime.utcnow(), GENESIS_TARGET,
  15. [], merkle_tree([]).get_hash(), 0)
  16. GENESIS_BLOCK_HASH = GENESIS_BLOCK.hash
  17. class Blockchain:
  18. """
  19. A block chain: a ordered, immutable list of valid blocks. The only way to create a blockchain
  20. instance is the constructor, which will create a block chain containing only the genesis block,
  21. and the `try_append` method which creates a new block chain only if the given block is valid on
  22. top of `self`.
  23. :ivar blocks: The blocks in this chain, oldest first.
  24. :vartype blocks: List[Block]
  25. :ivar block_indices: A dictionary allowing efficient lookup of the index of a block in this
  26. block chain by its hash value.
  27. :vartype block_indices: Dict[bytes, int]
  28. :ivar unspent_coins: A dictionary mapping from (allowed/available) transaction inputs
  29. to the transaction output that created this coin.
  30. :vartype unspent_coins: Dict[TransactionInput, TransactionTarget]
  31. """
  32. def __init__(self):
  33. self.blocks = [GENESIS_BLOCK]
  34. assert self.blocks[0].height == 0
  35. self.block_indices = {GENESIS_BLOCK_HASH: 0}
  36. self.unspent_coins = {}
  37. self.total_difficulty = 0
  38. def try_append(self, block: 'Block') -> 'Optional[Blockchain]':
  39. """
  40. If `block` is valid on top of this chain, returns a new blockchain including that block.
  41. Otherwise, it returns `None`.
  42. """
  43. if not block.verify(self.head, self.compute_target_next_block(), self.unspent_coins, self.block_indices,
  44. compute_blockreward_next_block(self.head.height)):
  45. return None
  46. unspent_coins = self.unspent_coins.copy()
  47. for t in block.transactions:
  48. for inp in t.inputs:
  49. if inp.is_coinbase:
  50. continue
  51. # the checks for tx using the same inputs are already done in the block.verify method
  52. unspent_coins.pop((inp.transaction_hash, inp.output_idx), None)
  53. for i, target in enumerate(t.targets):
  54. if target.is_pay_to_pubkey or target.is_pay_to_pubkey_lock:
  55. unspent_coins[(t.get_hash(), i)] = target
  56. chain = Blockchain()
  57. chain.unspent_coins = unspent_coins
  58. chain.blocks = self.blocks + [block]
  59. chain.block_indices = self.block_indices.copy()
  60. chain.block_indices[block.hash] = len(self.blocks)
  61. chain.total_difficulty = self.total_difficulty + GENESIS_TARGET - block.target
  62. return chain
  63. def get_block_by_hash(self, hash_val: bytes) -> 'Optional[Block]':
  64. """ Returns a block by its hash value, or None if it cannot be found. """
  65. idx = self.block_indices.get(hash_val)
  66. if idx is None:
  67. return None
  68. return self.blocks[idx]
  69. @property
  70. def head(self):
  71. """
  72. The head of this block chain.
  73. :rtype: Block
  74. """
  75. return self.blocks[-1]
  76. def compute_target_next_block(self) -> int:
  77. """ Compute the desired target for the block following this chain's `head`. """
  78. should_duration = DIFFICULTY_TIMEDELTA.total_seconds()
  79. if (self.head.height % DIFFICULTY_BLOCK_INTERVAL != 0) or (self.head.height == 0):
  80. return self.head.target
  81. past_block = self.blocks[self.head.height - DIFFICULTY_BLOCK_INTERVAL]
  82. last_duration = (self.head.time - past_block.time).total_seconds()
  83. diff_adjustment_factor = last_duration / should_duration
  84. prev_target = self.head.target
  85. new_target = prev_target * diff_adjustment_factor
  86. # the genesis target was very easy, making it easier means there was a pause
  87. # in mining, so we start over with the initial target
  88. if new_target > self.blocks[0].target:
  89. new_target = self.blocks[0].target
  90. return int(new_target)