aboutsummaryrefslogtreecommitdiff
path: root/qa/rpc-tests/test_framework/blockstore.py
blob: 4bc279032bf089c4745188d6750c8a7bbbf82ed9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python3
# BlockStore: a helper class that keeps a map of blocks and implements
#             helper functions for responding to getheaders and getdata,
#             and for constructing a getheaders message
#

from .mininode import *
from io import BytesIO
import dbm.ndbm

class BlockStore(object):
    def __init__(self, datadir):
        self.blockDB = dbm.ndbm.open(datadir + "/blocks", 'c')
        self.currentBlock = 0
        self.headers_map = dict()
    
    def close(self):
        self.blockDB.close()

    def get(self, blockhash):
        serialized_block = None
        try:
            serialized_block = self.blockDB[repr(blockhash)]
        except KeyError:
            return None
        f = BytesIO(serialized_block)
        ret = CBlock()
        ret.deserialize(f)
        ret.calc_sha256()
        return ret

    def get_header(self, blockhash):
        try:
            return self.headers_map[blockhash]
        except KeyError:
            return None

    # Note: this pulls full blocks out of the database just to retrieve
    # the headers -- perhaps we could keep a separate data structure
    # to avoid this overhead.
    def headers_for(self, locator, hash_stop, current_tip=None):
        if current_tip is None:
            current_tip = self.currentBlock
        current_block_header = self.get_header(current_tip)
        if current_block_header is None:
            return None

        response = msg_headers()
        headersList = [ current_block_header ]
        maxheaders = 2000
        while (headersList[0].sha256 not in locator.vHave):
            prevBlockHash = headersList[0].hashPrevBlock
            prevBlockHeader = self.get_header(prevBlockHash)
            if prevBlockHeader is not None:
                headersList.insert(0, prevBlockHeader)
            else:
                break
        headersList = headersList[:maxheaders] # truncate if we have too many
        hashList = [x.sha256 for x in headersList]
        index = len(headersList)
        if (hash_stop in hashList):
            index = hashList.index(hash_stop)+1
        response.headers = headersList[:index]
        return response

    def add_block(self, block):
        block.calc_sha256()
        try:
            self.blockDB[repr(block.sha256)] = bytes(block.serialize())
        except TypeError as e:
            print("Unexpected error: ", sys.exc_info()[0], e.args)
        self.currentBlock = block.sha256
        self.headers_map[block.sha256] = CBlockHeader(block)

    def add_header(self, header):
        self.headers_map[header.sha256] = header

    def get_blocks(self, inv):
        responses = []
        for i in inv:
            if (i.type == 2): # MSG_BLOCK
                block = self.get(i.hash)
                if block is not None:
                    responses.append(msg_block(block))
        return responses

    def get_locator(self, current_tip=None):
        if current_tip is None:
            current_tip = self.currentBlock
        r = []
        counter = 0
        step = 1
        lastBlock = self.get(current_tip)
        while lastBlock is not None:
            r.append(lastBlock.hashPrevBlock)
            for i in range(step):
                lastBlock = self.get(lastBlock.hashPrevBlock)
                if lastBlock is None:
                    break
            counter += 1
            if counter > 10:
                step *= 2
        locator = CBlockLocator()
        locator.vHave = r
        return locator

class TxStore(object):
    def __init__(self, datadir):
        self.txDB = dbm.ndbm.open(datadir + "/transactions", 'c')

    def close(self):
        self.txDB.close()

    def get(self, txhash):
        serialized_tx = None
        try:
            serialized_tx = self.txDB[repr(txhash)]
        except KeyError:
            return None
        f = BytesIO(serialized_tx)
        ret = CTransaction()
        ret.deserialize(f)
        ret.calc_sha256()
        return ret

    def add_transaction(self, tx):
        tx.calc_sha256()
        try:
            self.txDB[repr(tx.sha256)] = bytes(tx.serialize())
        except TypeError as e:
            print("Unexpected error: ", sys.exc_info()[0], e.args)

    def get_transactions(self, inv):
        responses = []
        for i in inv:
            if (i.type == 1): # MSG_TX
                tx = self.get(i.hash)
                if tx is not None:
                    responses.append(msg_tx(tx))
        return responses