2 # Copyright (c) 2015-2017 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."""
7 from .mininode
import *
9 import dbm
.dumb
as dbmd
11 logger
= logging
.getLogger("TestFramework.blockstore")
14 """BlockStore helper class.
16 BlockStore keeps a map of blocks and implements helper functions for
17 responding to getheaders and getdata, and for constructing a getheaders
21 def __init__(self
, datadir
):
22 self
.blockDB
= dbmd
.open(datadir
+ "/blocks", 'c')
24 self
.headers_map
= dict()
29 def erase(self
, blockhash
):
30 del self
.blockDB
[repr(blockhash
)]
32 # lookup an entry and return the item as raw bytes
33 def get(self
, blockhash
):
36 value
= self
.blockDB
[repr(blockhash
)]
41 # lookup an entry and return it as a CBlock
42 def get_block(self
, blockhash
):
44 serialized_block
= self
.get(blockhash
)
45 if serialized_block
is not None:
46 f
= BytesIO(serialized_block
)
52 def get_header(self
, blockhash
):
54 return self
.headers_map
[blockhash
]
58 # Note: this pulls full blocks out of the database just to retrieve
59 # the headers -- perhaps we could keep a separate data structure
60 # to avoid this overhead.
61 def headers_for(self
, locator
, hash_stop
, current_tip
=None):
62 if current_tip
is None:
63 current_tip
= self
.currentBlock
64 current_block_header
= self
.get_header(current_tip
)
65 if current_block_header
is None:
68 response
= msg_headers()
69 headersList
= [ current_block_header
]
71 while (headersList
[0].sha256
not in locator
.vHave
):
72 prevBlockHash
= headersList
[0].hashPrevBlock
73 prevBlockHeader
= self
.get_header(prevBlockHash
)
74 if prevBlockHeader
is not None:
75 headersList
.insert(0, prevBlockHeader
)
78 headersList
= headersList
[:maxheaders
] # truncate if we have too many
79 hashList
= [x
.sha256
for x
in headersList
]
80 index
= len(headersList
)
81 if (hash_stop
in hashList
):
82 index
= hashList
.index(hash_stop
)+1
83 response
.headers
= headersList
[:index
]
86 def add_block(self
, block
):
89 self
.blockDB
[repr(block
.sha256
)] = bytes(block
.serialize())
90 except TypeError as e
:
91 logger
.exception("Unexpected error")
92 self
.currentBlock
= block
.sha256
93 self
.headers_map
[block
.sha256
] = CBlockHeader(block
)
95 def add_header(self
, header
):
96 self
.headers_map
[header
.sha256
] = header
98 # lookup the hashes in "inv", and return p2p messages for delivering
100 def get_blocks(self
, inv
):
103 if (i
.type == 2 or i
.type == (2 |
(1 << 30))): # MSG_BLOCK or MSG_WITNESS_BLOCK
104 data
= self
.get(i
.hash)
106 # Use msg_generic to avoid re-serialization
107 responses
.append(msg_generic(b
"block", data
))
110 def get_locator(self
, current_tip
=None):
111 if current_tip
is None:
112 current_tip
= self
.currentBlock
116 lastBlock
= self
.get_block(current_tip
)
117 while lastBlock
is not None:
118 r
.append(lastBlock
.hashPrevBlock
)
119 for i
in range(step
):
120 lastBlock
= self
.get_block(lastBlock
.hashPrevBlock
)
121 if lastBlock
is None:
126 locator
= CBlockLocator()
131 def __init__(self
, datadir
):
132 self
.txDB
= dbmd
.open(datadir
+ "/transactions", 'c')
137 # lookup an entry and return the item as raw bytes
138 def get(self
, txhash
):
141 value
= self
.txDB
[repr(txhash
)]
146 def add_transaction(self
, tx
):
149 self
.txDB
[repr(tx
.sha256
)] = bytes(tx
.serialize())
150 except TypeError as e
:
151 logger
.exception("Unexpected error")
153 def get_transactions(self
, inv
):
156 if (i
.type == 1 or i
.type == (1 |
(1 << 30))): # MSG_TX or MSG_WITNESS_TX
157 tx
= self
.get(i
.hash)
159 responses
.append(msg_generic(b
"tx", tx
))