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