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.4KB

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