block.py 9.1 KB

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