-
Notifications
You must be signed in to change notification settings - Fork 15
/
regnet.py
200 lines (166 loc) · 8.73 KB
/
regnet.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""
Regnet specific functions and settings
"""
import base64
import math
import os
import sqlite3
import sys
import time
import functools
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA
from Cryptodome.Signature import PKCS1_v1_5
from hashlib import sha224
from random import getrandbits
import connections
import mempool as mp
import mining_heavy3 as mining
# fixed diff for regnet
REGNET_DIFF = 16
REGNET_PORT = 3030
REGNET_DB = "static/regmode.db"
REGNET_INDEX = "static/index_reg.db"
REGNET_PEERS = "peers_reg.txt"
REGNET_SUGGESTED_PEERS = "peers_reg.txt"
SQL_INDEX = [ "CREATE TABLE aliases (block_height INTEGER, address, alias)",
"CREATE TABLE tokens (block_height INTEGER, timestamp, token, address, recipient, txid, amount INTEGER)" ]
SQL_LEDGER = [ "CREATE TABLE misc (block_height INTEGER, difficulty TEXT)",
"CREATE TABLE transactions (block_height INTEGER, timestamp NUMERIC, address TEXT, recipient TEXT, \
amount NUMERIC, signature TEXT, public_key TEXT, block_hash TEXT, fee NUMERIC, reward NUMERIC, \
operation TEXT, openfield TEXT)",
"INSERT INTO transactions (openfield, operation, reward, fee, block_hash, public_key, signature, \
amount, recipient, address, timestamp, block_height) \
VALUES ('genesis', 1, 1, 0, '7a0f384876aca3871adbde8622a87f8b971ede0ed8ee10425e3958a1', \
'-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKvLTbDx85a1ugb/6xMMhVOq6U\n2GeYT8+Iq2z9FwIMR40l2ttGqNK7varNccFLIu8Kn4ogDQs3WSWQCxNkhZh/FqzF\nYYa3/ItPPfzrXqgajwD8q4Zt4Ymjt8+2BkImPjjFNkuTQIz2Iu3yFqOIxLdjMw7n\nUVu9tFPiUkD0VnDPLQIDAQAB\n-----END PUBLIC KEY-----', \
'DKiWVr+GQHrsEUlu3qEQnsB5rznU4Is7RFLnPmHM1grobiUFHup0kSWiN83gBkNS9LgE57RXUEJvxMKc+9hIAzYE8EwGtO3RsXxkqPTT1v19CguN0iqE4nIM8Bur53/Djs5a1bH/R8EMersemZY1bDJ4jTeeba6yqFxmevGk/gw=', \
0, '4edadac9093d9326ee4b17f869b14f1a2534f96f9c5d7b48dc9acaed', 'genesis', 1493640955.47, 1);",
"INSERT INTO misc (difficulty, block_height) VALUES ({},1)".format(REGNET_DIFF)]
FILES_TO_REMOVE = [REGNET_DB, REGNET_INDEX]
HASHCOUNT = 10
# Max number of tx to embed per block.
TX_PER_BLOCK = 2
# Do not edit below, it's fed by node.py
ADDRESS = 'This is a fake address placeholder for regtest mode only'
KEY = None
PRIVATE_KEY_READABLE = 'matching priv key'
PUBLIC_KEY_B64ENCODED = 'matching pub key b64'
DIGEST_BLOCK = None
# because of compatibility - huge node refactor wanted.
def sql_trace_callback(log, id, statement):
line = f"SQL[{id}] {statement}"
log.warning(line)
def generate_one_block(blockhash, mempool_txs, node, db_handler):
try:
if not blockhash:
node.logger.app_log.warning("Bad blockhash")
return
diff_hex = math.floor((REGNET_DIFF / 8) - 1)
mining_condition = blockhash[0:diff_hex]
while True:
try_arr = [('%0x' % getrandbits(32)) for i in range(HASHCOUNT)]
i = 0
for i in range(100):
i += 1
seed = ('%0x' % getrandbits(128 - 32))
prefix = ADDRESS + seed
# print("node heavy", node.heavy)
if node.heavy:
possibles = [nonce for nonce in try_arr if
mining_condition in (
mining.anneal3(mining.MMAP,
int.from_bytes(
sha224((prefix + nonce + blockhash)
.encode("utf-8")).digest(), 'big')))]
else:
possibles = [nonce for nonce in try_arr if
mining_condition in (
mining.anneal3_regnet(mining.MMAP,
int.from_bytes(
sha224((prefix + nonce + blockhash)
.encode("utf-8")).digest(), 'big')))]
if possibles:
nonce = seed + possibles[0]
node.logger.app_log.warning("Generate got a block in {} tries len {}".format(i, len(possibles)))
# assemble block with mp data
txs = []
for i in range(TX_PER_BLOCK):
if not len(mempool_txs):
break
txs.append(mempool_txs.pop(0))
block_send = []
removal_signature = []
for mpdata in txs:
transaction = (
str(mpdata[0]), str(mpdata[1][:56]), str(mpdata[2][:56]), '%.8f' % float(mpdata[3]),
str(mpdata[4]), str(mpdata[5]), str(mpdata[6]),
str(mpdata[7])) # create tuple
# node.logger.app_log.warning transaction
block_send.append(transaction) # append tuple to list for each run
removal_signature.append(str(mpdata[4])) # for removal after successful mining
# claim reward
block_timestamp = '%.2f' % time.time()
transaction_reward = (str(block_timestamp), str(ADDRESS[:56]), str(ADDRESS[:56]),
'%.8f' % float(0), "0", str(nonce)) # only this part is signed!
# node.logger.app_log.warning transaction_reward
hash = SHA.new(str(transaction_reward).encode("utf-8"))
signer = PKCS1_v1_5.new(KEY)
signature = signer.sign(hash)
signature_enc = base64.b64encode(signature)
if signer.verify(hash, signature):
node.logger.app_log.warning("Signature valid")
block_send.append((str(block_timestamp), str(ADDRESS[:56]), str(ADDRESS[:56]), '%.8f' % float(0),
str(signature_enc.decode("utf-8")), str(PUBLIC_KEY_B64ENCODED.decode("utf-8")),
"0", str(nonce))) # mining reward tx
node.logger.app_log.warning("Block to send: {}".format(block_send))
# calc hash
new_hash = DIGEST_BLOCK(node, [block_send], None, 'regtest', db_handler)
# post block to self or better, send to db to make sure it is. when we add the next one?
# use a link to the block digest function
# embed at mot TX_PER_BLOCK txs from the mp
return new_hash
except Exception as e:
node.logger.app_log.warning(e)
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
node.logger.app_log.warning(exc_type, fname, exc_tb.tb_lineno)
def command(sdef, data, blockhash, node, db_handler):
try:
node.logger.app_log.warning("Regnet got command {}".format(data))
if data == 'regtest_generate':
how_many = int(connections.receive(sdef))
node.logger.app_log.warning("regtest_generate {} {}".format(how_many, blockhash))
mempool_txs = mp.MEMPOOL.fetchall(mp.SQL_SELECT_TX_TO_SEND)
for i in range(how_many):
blockhash = generate_one_block(blockhash, mempool_txs, node, db_handler)
connections.send(sdef, 'OK')
except Exception as e:
node.logger.app_log.warning(e)
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
node.logger.app_log.warning(exc_type, fname, exc_tb.tb_lineno)
def init(app_log, trace_db_calls=False):
# Empty peers
with open(REGNET_PEERS, 'w') as f:
f.write("{}")
with open(REGNET_SUGGESTED_PEERS, 'w') as f:
f.write("{}")
# empty files
for remove_me in FILES_TO_REMOVE:
if os.path.exists(remove_me):
os.remove(remove_me)
# create empty index db
with sqlite3.connect(REGNET_DB) as source_db:
if trace_db_calls:
source_db.set_trace_callback(functools.partial(sql_trace_callback,app_log,"REGNET-INIT"))
for request in SQL_LEDGER:
source_db.execute(request)
source_db.commit()
# create empty reg db
with sqlite3.connect(REGNET_INDEX) as source_db:
if trace_db_calls:
source_db.set_trace_callback(functools.partial(sql_trace_callback,app_log,"REGNET-INIT-INDEX"))
for request in SQL_INDEX:
source_db.execute(request)
source_db.commit()
# Here, we do not have the keys info yet.