-
Notifications
You must be signed in to change notification settings - Fork 0
/
add_height.py
161 lines (139 loc) · 7.49 KB
/
add_height.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
from time import sleep
from neo4j import GraphDatabase
import logging
from logging.handlers import RotatingFileHandler
log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(funcName)s(%(lineno)d) %(message)s')
logFile = 'log/add_height.log'
my_handler = RotatingFileHandler(logFile, mode='a', maxBytes=10*1024*1024,backupCount=6, encoding=None, delay=0)
my_handler.setFormatter(log_formatter)
my_handler.setLevel(logging.INFO)
app_log = logging.getLogger('root')
app_log.setLevel(logging.INFO)
app_log.addHandler(my_handler)
class AddHeight(object):
def __init__(self, uri=None, user=None, password=None, driver=None):
if driver is None:
self._driver = GraphDatabase.driver(uri, auth=(user, password), encrypted=False, max_connection_lifetime=600)#REVISE LATER FOR ENCRYPTION!!!
else:
self._driver = driver
def close(self):
self._driver.close()
@staticmethod
def _add_height(tx, block_id,n):
last_data=""
for i in range(n):
try:
result = tx.run("MATCH (b:block {id:$block_id})-[:LINKS]->(n:block) "
"WITH b.height AS height, n AS next_block "
"SET next_block.height = height+1 "
"RETURN next_block.id ",
block_id=block_id)
data=result.data()
last_data=data
print(f"data: {data}, i {i}")
except:
print("\x1b[1;31;43m"+"Found possible stale blocks..."+ "\x1b[0m")
result = tx.run("MATCH (b:block {id:$block_id})-[:LINKS]->(next_block:block) "
"RETURN next_block.id ",
block_id=block_id)
data=result.data()
last_data=data
print(data)
break
if len(data)>1: break
elif len(data)==0: return True
block_id = data[0]["next_block.id"]
print(block_id)
if i==(n-1):
app_log.info(f"Finished with block {block_id}!")
return block_id
print("\x1b[1;31;43m"+"Dealing with stale block..."+ "\x1b[0m")
block_ids = last_data
print(f"candidate block ids {block_ids}")
for j in reversed(range(15)):
query = "MATCH (b:block {id:$block_id})-[:LINKS]->(x:block)"
for k in range(j):
query+="-[:LINKS]->(:block)"
query+="\nRETURN x.id"
scores = {}
for candidate in block_ids:
result = tx.run(query, block_id=candidate["next_block.id"])
data = result.data()
_score=0
if len(data)>0: _score=1
score = {candidate["next_block.id"]:_score}
print(f"score {score}. _score: {_score} result.data(): {data}")
scores.update(score)
print(f"scores: {scores}")
winner_block=""
xor = 0
for block,result in scores.items():
xor+=result
print(f"block: {block}. result: {result}. xor: {xor}")
if xor == 1:
for block,result in scores.items():
if result == 1:
winner_block=block
print(f"Found winner block {winner_block}")
break
print("fixing the height conflict...")
for candidate in block_ids:
if candidate["next_block.id"] != winner_block:
stale_blocks=[ candidate["next_block.id"] ]
for j in reversed(range(15)):
query = "MATCH (b:block {id:$block_id})"
for k in range(j):query+="-[:LINKS]->(:block)"
query+="-[:LINKS]->(x:block)\nRETURN x.id"
result = tx.run(query, block_id=candidate["next_block.id"])
data = result.data()
if len(data)>0:stale_blocks.append(data[0]["x.id"])
print(f"deleting block {candidate['next_block.id']} and its whole fork:{stale_blocks}")
for stale_block in stale_blocks:
result = tx.run("MATCH (o:output)-[x:CREATES]-(t:transaction)-[c]-(b:block {id:$block_id})-[r]-() \n"
"WHERE NOT (b)-[:CONTAINS]->(t)<-[:CONTAINS]-(:block) "
"OR (b)-[:COINBASE]->(t)<-[:COINBASE]-(:block) \n"
"OPTIONAL MATCH (o)-[w]-(:address) \n"
"OPTIONAL MATCH (:output)-[y:SPENDS]-(t) \n"
"DETACH DELETE b,c,r,o,x,t,w,y ", block_id=stale_block)
result = tx.run("MATCH (b:block {id:$block_id}) RETURN b", block_id=stale_block)
data = result.data()
print(f"does the block still exists? result.data: {data}")
if len(data)>0:
print(f"It does. So detach deleting now...")
result = tx.run("MATCH (b:block {id:$block_id}) "
"OPTIONAL MATCH (b)-[w]-(t:transaction)-[x]-(o) \n"
"WHERE NOT (b)-[:CONTAINS]->(t)<-[:CONTAINS]-(:block)"
"OPTIONAL MATCH (:output)-[y:SPENDS]-(t)--(b) \n"
"WHERE NOT (b)-[:CONTAINS]->(t)<-[:CONTAINS]-(:block)\n"
"DETACH DELETE b,w,t,x,o,y", block_id=stale_block)
else: print("deleted successfully")
else:
result = tx.run("MATCH (b:block {id:$block_id})<-[:LINKS]-(prev_block:block) \n"
"RETURN prev_block.id \n", block_id=candidate["next_block.id"])
_blkid = result.single().value()
print(f"_blkid: {_blkid}")
block_id = _blkid
break
if j==0:
print("\x1b[1;31;43m"+"Couldn't find the longest chain yet."+ "\x1b[0m")
return False
app_log.info(f"Finished with block {block_id}!")
return block_id
def add_height_batch(self,block_id=None,n=1000):
with self._driver.session() as session:
result = session.write_transaction(self._add_height, block_id,n)
#print(result)
return result
def add_height(self, db,block_id=None):
#If no specific block to start from, then we start with the genesis block.
if block_id is None: block_id="0000000000000000000000000000000000000000000000000000000000000000"
for i in range(400000):
print(i)
block_id = db.add_height_batch(block_id,n=5)
if isinstance(block_id,bool):
print("finished!")
break
app_log.info(f"Last block {block_id} with height {i*10000}")
def main(block_id=None):
db = AddHeight("neo4j://10.0.0.30:7687", "neo4j", "wallet")
db.add_height(db,block_id)