You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

blockstore.py 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2015-2016 The Bitcoin Core developers
  3. # Distributed under the MIT software license, see the accompanying
  4. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. # BlockStore: a helper class that keeps a map of blocks and implements
  6. # helper functions for responding to getheaders and getdata,
  7. # and for constructing a getheaders message
  8. #
  9. from .mininode import *
  10. from io import BytesIO
  11. import dbm.dumb as dbmd
  12. class BlockStore(object):
  13. def __init__(self, datadir):
  14. self.blockDB = dbmd.open(datadir + "/blocks", 'c')
  15. self.currentBlock = 0
  16. self.headers_map = dict()
  17. def close(self):
  18. self.blockDB.close()
  19. def erase(self, blockhash):
  20. del self.blockDB[repr(blockhash)]
  21. # lookup an entry and return the item as raw bytes
  22. def get(self, blockhash):
  23. value = None
  24. try:
  25. value = self.blockDB[repr(blockhash)]
  26. except KeyError:
  27. return None
  28. return value
  29. # lookup an entry and return it as a CBlock
  30. def get_block(self, blockhash):
  31. ret = None
  32. serialized_block = self.get(blockhash)
  33. if serialized_block is not None:
  34. f = BytesIO(serialized_block)
  35. ret = CBlock()
  36. ret.deserialize(f)
  37. ret.calc_sha256()
  38. return ret
  39. def get_header(self, blockhash):
  40. try:
  41. return self.headers_map[blockhash]
  42. except KeyError:
  43. return None
  44. # Note: this pulls full blocks out of the database just to retrieve
  45. # the headers -- perhaps we could keep a separate data structure
  46. # to avoid this overhead.
  47. def headers_for(self, locator, hash_stop, current_tip=None):
  48. if current_tip is None:
  49. current_tip = self.currentBlock
  50. current_block_header = self.get_header(current_tip)
  51. if current_block_header is None:
  52. return None
  53. response = msg_headers()
  54. headersList = [ current_block_header ]
  55. maxheaders = 2000
  56. while (headersList[0].sha256 not in locator.vHave):
  57. prevBlockHash = headersList[0].hashPrevBlock
  58. prevBlockHeader = self.get_header(prevBlockHash)
  59. if prevBlockHeader is not None:
  60. headersList.insert(0, prevBlockHeader)
  61. else:
  62. break
  63. headersList = headersList[:maxheaders] # truncate if we have too many
  64. hashList = [x.sha256 for x in headersList]
  65. index = len(headersList)
  66. if (hash_stop in hashList):
  67. index = hashList.index(hash_stop)+1
  68. response.headers = headersList[:index]
  69. return response
  70. def add_block(self, block):
  71. block.calc_sha256()
  72. try:
  73. self.blockDB[repr(block.sha256)] = bytes(block.serialize())
  74. except TypeError as e:
  75. print("Unexpected error: ", sys.exc_info()[0], e.args)
  76. self.currentBlock = block.sha256
  77. self.headers_map[block.sha256] = CBlockHeader(block)
  78. def add_header(self, header):
  79. self.headers_map[header.sha256] = header
  80. # lookup the hashes in "inv", and return p2p messages for delivering
  81. # blocks found.
  82. def get_blocks(self, inv):
  83. responses = []
  84. for i in inv:
  85. if (i.type == 2): # MSG_BLOCK
  86. data = self.get(i.hash)
  87. if data is not None:
  88. # Use msg_generic to avoid re-serialization
  89. responses.append(msg_generic(b"block", data))
  90. return responses
  91. def get_locator(self, current_tip=None):
  92. if current_tip is None:
  93. current_tip = self.currentBlock
  94. r = []
  95. counter = 0
  96. step = 1
  97. lastBlock = self.get_block(current_tip)
  98. while lastBlock is not None:
  99. r.append(lastBlock.hashPrevBlock)
  100. for i in range(step):
  101. lastBlock = self.get_block(lastBlock.hashPrevBlock)
  102. if lastBlock is None:
  103. break
  104. counter += 1
  105. if counter > 10:
  106. step *= 2
  107. locator = CBlockLocator()
  108. locator.vHave = r
  109. return locator
  110. class TxStore(object):
  111. def __init__(self, datadir):
  112. self.txDB = dbmd.open(datadir + "/transactions", 'c')
  113. def close(self):
  114. self.txDB.close()
  115. # lookup an entry and return the item as raw bytes
  116. def get(self, txhash):
  117. value = None
  118. try:
  119. value = self.txDB[repr(txhash)]
  120. except KeyError:
  121. return None
  122. return value
  123. def get_transaction(self, txhash):
  124. ret = None
  125. serialized_tx = self.get(txhash)
  126. if serialized_tx is not None:
  127. f = BytesIO(serialized_tx)
  128. ret = CTransaction()
  129. ret.deserialize(f)
  130. ret.calc_sha256()
  131. return ret
  132. def add_transaction(self, tx):
  133. tx.calc_sha256()
  134. try:
  135. self.txDB[repr(tx.sha256)] = bytes(tx.serialize())
  136. except TypeError as e:
  137. print("Unexpected error: ", sys.exc_info()[0], e.args)
  138. def get_transactions(self, inv):
  139. responses = []
  140. for i in inv:
  141. if (i.type == 1): # MSG_TX
  142. tx = self.get(i.hash)
  143. if tx is not None:
  144. responses.append(msg_generic(b"tx", tx))
  145. return responses