This repository has been archived by the owner on May 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathblock_storage.py
139 lines (112 loc) · 4.52 KB
/
block_storage.py
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
import os
import sys
import typing
import importlib.util
import google.protobuf.json_format
import binascii
import hashlib
from schema_version import *
SCHEMA_DIR = 'schema'
class BlockStorageBase:
@staticmethod
def get_block_hash(block) -> bytes:
'''@return hex hash of the block.
pray that it matches the one calculated in iroha!
(there are no guarantees of identical serialization of protobuf)
'''
hasher = hashlib.sha3_256()
hasher.update(block.block_v1.payload.SerializeToString())
return binascii.hexlify(hasher.digest())
class BlockStorageFiles(BlockStorageBase):
def __init__(self, path, schema):
self._path = path
self._schema = schema
def iterate(self):
height = 1
while True:
block = self.load_at_height(height)
if block is None:
break
yield block
height += 1
def _get_block_file_path_at_height(self, height: int) -> str:
return os.path.join(self._path, '{0:0>16}'.format(height))
def load_at_height(self, height: int):
block_file_path = self._get_block_file_path_at_height(height)
if not os.path.isfile(block_file_path):
return None
with open(block_file_path, 'rt') as block_file:
return google.protobuf.json_format.Parse(block_file.read(),
self._schema.Block())
def get_top_block_height(self) -> typing.Optional[int]:
# listing a directory with very many files is longer than querying single file names
block_exists = lambda height: os.path.isfile(
self._get_block_file_path_at_height(height))
if not block_exists(1): return None
scope = [1, 1]
# incremental search
while block_exists(scope[1]):
scope = [scope[1], scope[1] * 2]
# binary search
while scope[0] + 1 < scope[1]:
p = scope[0] + (scope[1] - scope[0]) // 2
if block_exists(p):
scope[0] = p
else:
scope[1] = p
assert block_exists(scope[0])
assert not block_exists(scope[1])
return scope[0]
class BlockStorageSql(BlockStorageBase):
def __init__(self, cursor, schema):
self._cursor = cursor
self._schema = schema
def _block_from_hex(self, block_hex):
block = self._schema.Block()
block.ParseFromString(binascii.unhexlify(block_hex))
return block
def iterate(self):
self._cursor.execute(
'select block_data from blocks order by height asc')
while True:
row = self._cursor.fetchone()
if row is None:
break
yield self._block_from_hex(row[0])
def load_at_height(self, height: int):
self._cursor.execute('select block_data from blocks where height = %s',
height)
rows = self._cursor.fetchall()
if len(row) == 0:
return None
return self._block_from_hex(rows[0][0])
def get_top_block_height(self) -> typing.Optional[int]:
self._cursor.execute('select max(height) from blocks')
rows = self._cursor.fetchall()
if len(row) == 0:
return None
return self._block_from_hex(rows[0][0])
def load_block_schema(version: SchemaVersion):
schema_package_path = os.path.join(
SCHEMA_DIR, '{}_{}_{}'.format(version.iroha_major, version.iroha_minor,
version.iroha_patch))
if not os.path.isdir(schema_package_path):
raise Exception(
'Schema files not found in {}.'.format(schema_package_path))
sys.path.append(schema_package_path)
block_module_path = os.path.join(schema_package_path, 'block_pb2.py')
block_module_spec = importlib.util.spec_from_file_location(
'block_pb2', block_module_path)
block_module = importlib.util.module_from_spec(block_module_spec)
try:
block_module_spec.loader.exec_module(block_module)
except Exception as e:
raise Exception('Could not load schema module {}: {}'.format(
block_module_path, str(e))) from e
return block_module
def get_block_storage(block_storage_files_path: typing.Optional[str], cursor,
version: SchemaVersion):
block_schema = load_block_schema(version)
if block_storage_files_path is not None:
return BlockStorageFiles(block_storage_files_path, block_schema)
return BlockStorageSql(cursor, block_schema)