block.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. """ Definitions of blocks, and the genesis block. """
  2. from datetime import datetime, timedelta
  3. from binascii import hexlify, unhexlify
  4. from struct import pack
  5. import json
  6. import logging
  7. import math
  8. from .merkle import merkle_tree
  9. from .crypto import get_hasher
  10. __all__ = ['Block', 'GENESIS_BLOCK', 'GENESIS_BLOCK_HASH']
  11. class Block:
  12. """
  13. A block.
  14. :ivar hash: The hash value of this block.
  15. :vartype hash: bytes
  16. :ivar prev_block_hash: The hash of the previous block.
  17. :vartype prev_block_hash: bytes
  18. :ivar merkle_root_hash: The hash of the merkle tree root of the transactions in this block.
  19. :vartype merkle_root_hash: bytes
  20. :ivar time: The time when this block was created.
  21. :vartype time: datetime
  22. :ivar nonce: The nonce in this block that was required to achieve the proof of work.
  23. :vartype nonce: int
  24. :ivar height: The height (accumulated difficulty) of this block.
  25. :vartype height: int
  26. :ivar received_time: The time when we received this block.
  27. :vartype received_time: datetime
  28. :ivar difficulty: The difficulty of this block.
  29. :vartype difficulty: int
  30. :ivar transactions: The list of transactions in this block.
  31. :vartype transactions: List[Transaction]
  32. """
  33. def __init__(self, prev_block_hash, time, nonce, height, received_time, difficulty, transactions, merkle_root_hash=None):
  34. self.prev_block_hash = prev_block_hash
  35. self.merkle_root_hash = merkle_root_hash
  36. self.time = time
  37. self.nonce = nonce
  38. self.height = height
  39. self.received_time = received_time
  40. self.difficulty = difficulty
  41. self.transactions = transactions
  42. self.hash = self._get_hash()
  43. def to_json_compatible(self):
  44. """ Returns a JSON-serializable representation of this object. """
  45. val = {}
  46. val['prev_block_hash'] = hexlify(self.prev_block_hash).decode()
  47. val['merkle_root_hash'] = hexlify(self.merkle_root_hash).decode()
  48. val['time'] = self.time.strftime("%Y-%m-%dT%H:%M:%S.%f UTC")
  49. val['nonce'] = self.nonce
  50. val['height'] = self.height
  51. val['difficulty'] = self.difficulty
  52. val['transactions'] = [t.to_json_compatible() for t in self.transactions]
  53. return val
  54. @classmethod
  55. def from_json_compatible(cls, val):
  56. """ Create a new block from its JSON-serializable representation. """
  57. from .transaction import Transaction
  58. return cls(unhexlify(val['prev_block_hash']),
  59. datetime.strptime(val['time'], "%Y-%m-%dT%H:%M:%S.%f UTC"),
  60. int(val['nonce']),
  61. int(val['height']),
  62. datetime.utcnow(),
  63. int(val['difficulty']),
  64. [Transaction.from_json_compatible(t) for t in list(val['transactions'])],
  65. unhexlify(val['merkle_root_hash']))
  66. @classmethod
  67. def create(cls, blockchain: 'Blockchain', transactions: list, ts=None):
  68. """
  69. Create a new block for a certain blockchain, containing certain transactions.
  70. """
  71. tree = merkle_tree(transactions)
  72. difficulty = blockchain.compute_difficulty_next_block()
  73. if ts is None:
  74. ts = datetime.utcnow()
  75. if ts <= blockchain.head.time:
  76. ts = blockchain.head.time + timedelta(microseconds=1)
  77. return Block(blockchain.head.hash, ts, 0, blockchain.head.height + difficulty,
  78. None, difficulty, transactions, tree.get_hash())
  79. def __str__(self):
  80. return json.dumps(self.to_json_compatible(), indent=4)
  81. @staticmethod
  82. def _int_to_bytes(val: int) -> bytes:
  83. """ Turns an (arbitrarily long) integer into a bytes sequence. """
  84. l = val.bit_length() + 1
  85. # we need to include the length in the hash in some way, otherwise e.g.
  86. # the numbers (0xffff, 0x00) would be encoded identically to (0xff, 0xff00)
  87. return pack("<Q", l) + val.to_bytes(l, 'little', signed=True)
  88. def get_partial_hash(self):
  89. """
  90. Computes a hash over the contents of this block, except for the nonce. The proof of
  91. work can use this partial hash to efficiently try different nonces. Other uses should
  92. use `hash` to get the complete hash.
  93. """
  94. hasher = get_hasher()
  95. hasher.update(self.prev_block_hash)
  96. hasher.update(self.merkle_root_hash)
  97. hasher.update(self.time.strftime("%Y-%m-%dT%H:%M:%S.%f UTC").encode())
  98. hasher.update(self._int_to_bytes(self.difficulty))
  99. return hasher
  100. def finish_hash(self, hasher):
  101. """
  102. Finishes the hash in `hasher` with the nonce in this block. The proof of
  103. work can use this function to efficiently try different nonces. Other uses should
  104. use `hash` to get the complete hash in one step.
  105. """
  106. hasher.update(self._int_to_bytes(self.nonce))
  107. return hasher.digest()
  108. def _get_hash(self):
  109. """ Compute the hash of the header data. This is not necessarily the received hash value for this block! """
  110. hasher = self.get_partial_hash()
  111. return self.finish_hash(hasher)
  112. def verify_merkle(self):
  113. """ Verify that the merkle root hash is correct for the transactions in this block. """
  114. return merkle_tree(self.transactions).get_hash() == self.merkle_root_hash
  115. def verify_difficulty(self):
  116. """ Verifies that the hash value is correct and fulfills its difficulty promise. """
  117. if self.hash == GENESIS_BLOCK_HASH:
  118. return True
  119. if not verify_proof_of_work(self):
  120. logging.warning("block does not satisfy proof of work")
  121. return False
  122. return True
  123. def verify_prev_block(self, chain: 'Blockchain'):
  124. """ Verifies that the previous block pointer points to the head of the given block chain and difficulty and height are correct. """
  125. if chain.head.hash != self.prev_block_hash:
  126. logging.warning("Previous block is not head of the block chain.")
  127. return False
  128. if self.difficulty != chain.compute_difficulty_next_block():
  129. logging.warning("Block has wrong difficulty.")
  130. return False
  131. if chain.head.height + self.difficulty != self.height:
  132. logging.warning("Block has wrong height.")
  133. return False
  134. return True
  135. def verify_transactions(self, chain: 'Blockchain'):
  136. """ Verifies that all transaction in this block are valid in the given block chain. """
  137. mining_reward = None
  138. trans_set = set(self.transactions)
  139. for t in self.transactions:
  140. if not t.inputs:
  141. if mining_reward is not None:
  142. logging.warning("block has more than one reward transaction")
  143. return False
  144. mining_reward = t
  145. if not t.verify(chain, trans_set - {t}):
  146. return False
  147. if mining_reward is not None:
  148. fees = sum(t.get_transaction_fee(chain) for t in self.transactions)
  149. reward = chain.compute_blockreward_next_block()
  150. used = sum(t.amount for t in mining_reward.targets)
  151. if used > fees + reward:
  152. logging.warning("mining reward is too large")
  153. return False
  154. return True
  155. def verify_time(self, chain: 'Blockchain'):
  156. """
  157. Verifies that blocks are not from far in the future, but a bit younger
  158. than the head of `chain`.
  159. """
  160. if self.time - timedelta(hours=2) > datetime.utcnow():
  161. logging.warning("discarding block because it is from the far future")
  162. return False
  163. if self.time <= chain.head.time:
  164. logging.warning("discarding block because it is younger than its predecessor")
  165. return False
  166. return True
  167. def verify(self, chain: 'Blockchain'):
  168. """
  169. Verifies that this block contains only valid data and can be applied on top of the block
  170. chain `chain`.
  171. """
  172. assert self.hash not in chain.block_indices
  173. if self.height == 0:
  174. logging.warning("only the genesis block may have height=0")
  175. return False
  176. return self.verify_difficulty() and self.verify_merkle() and self.verify_prev_block(chain) \
  177. and self.verify_transactions(chain) and self.verify_time(chain)
  178. from .proof_of_work import verify_proof_of_work, GENESIS_DIFFICULTY, DIFFICULTY_BLOCK_INTERVAL, \
  179. DIFFICULTY_TARGET_TIMEDELTA
  180. GENESIS_BLOCK = Block("None; {} {}".format(DIFFICULTY_BLOCK_INTERVAL,
  181. DIFFICULTY_TARGET_TIMEDELTA).encode(), datetime(2017, 3, 3, 10, 35, 26, 922898), 0, 0,
  182. datetime.utcnow(), GENESIS_DIFFICULTY, [], merkle_tree([]).get_hash())
  183. GENESIS_BLOCK_HASH = GENESIS_BLOCK.hash
  184. from .blockchain import Blockchain