Skip to content

Commit 1e6df94

Browse files
authored
Merge branch 'dev' into btc-teleport
2 parents d1dcbb9 + ef02b02 commit 1e6df94

File tree

19 files changed

+978
-274
lines changed

19 files changed

+978
-274
lines changed

modules/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ class Setting:
6262
'auto_backup': Setting('validator', None, 'Make validator backup every election'),
6363
'auto_backup_path': Setting('validator', '/tmp/mytoncore/auto_backups/', 'Path to store auto-backups'),
6464
'prometheus_url': Setting('prometheus', None, 'Prometheus pushgateway url'),
65-
'onlyNode': Setting(None, None, 'MyTonCtrl will work only for collecting validator telemetry (if `sendTelemetry` is True), without participating in Elections and etc.')
65+
'onlyNode': Setting(None, None, 'MyTonCtrl will work only for collecting validator telemetry (if `sendTelemetry` is True), without participating in Elections and etc.'),
66+
'importGc': Setting(None, None, 'Delete imported archive blocks files. Restart mytoncore to apply this setting'),
6667
}
6768

6869

modules/alert_bot.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ def init_alerts():
116116
"Found proposals with hashes `{hashes}` that have significant amount of votes, but current validator didn't vote for them. Please check @tonstatus for more details.",
117117
VALIDATION_PERIOD
118118
),
119+
"initial_sync_completed": Alert(
120+
"info",
121+
"Initial sync has been completed (info alert with no sound)",
122+
"Node initial sync has been completed",
123+
0
124+
)
119125
}
120126

121127

@@ -134,6 +140,7 @@ def __init__(self, ton, local, *args, **kwargs):
134140
self.token = None
135141
self.chat_id = None
136142
self.last_db_check = 0
143+
self.initial_sync = None
137144

138145
def send_message(self, text: str, silent: bool = False, disable_web_page_preview: bool = False):
139146
if self.token is None:
@@ -207,6 +214,7 @@ def init(self):
207214
self.wallet = self.ton.GetValidatorWallet().addrB64
208215
self.ip = self.ton.get_node_ip()
209216
self.set_global_vars()
217+
self.initial_sync = self.ton.in_initial_sync()
210218
init_alerts()
211219
self.inited = True
212220

@@ -271,6 +279,7 @@ def setup_alert_bot(self, args):
271279
self.send_welcome_message()
272280
self.ton.local.db['BotToken'] = args[0]
273281
self.ton.local.db['ChatId'] = args[1]
282+
self.ton.local.save()
274283
color_print("setup_alert_bot - {green}OK{endc}")
275284
except Exception as e:
276285
self.local.add_log(f"Error while sending welcome message: {e}", "error")
@@ -314,7 +323,7 @@ def check_validator_wallet_balance(self):
314323
return
315324
validator_wallet = self.ton.GetValidatorWallet()
316325
validator_account = self.ton.GetAccount(validator_wallet.addrB64)
317-
if validator_account.balance < 10:
326+
if validator_account.status != "empty" and validator_account.balance < 10:
318327
self.send_alert("low_wallet_balance", wallet=validator_wallet.addrB64, balance=validator_account.balance)
319328

320329
def check_efficiency(self):
@@ -334,12 +343,12 @@ def check_efficiency(self):
334343

335344
def check_validator_working(self):
336345
validator_status = self.ton.GetValidatorStatus()
337-
if not validator_status.is_working:
346+
if not self.initial_sync and not validator_status.is_working:
338347
self.send_alert("service_down")
339348

340349
def check_sync(self):
341350
validator_status = self.ton.GetValidatorStatus()
342-
if validator_status.is_working and validator_status.out_of_sync >= 20:
351+
if not self.initial_sync and validator_status.is_working and validator_status.out_of_sync >= 20:
343352
self.send_alert("out_of_sync", sync=validator_status.out_of_sync)
344353

345354
def check_zero_blocks_created(self):
@@ -435,6 +444,13 @@ def check_voting(self):
435444
if need_to_vote:
436445
self.send_alert("voting", hashes=' '.join(need_to_vote))
437446

447+
def check_initial_sync(self):
448+
if not self.initial_sync:
449+
return
450+
if not self.ton.in_initial_sync():
451+
self.initial_sync = False
452+
self.send_alert("initial_sync_completed")
453+
438454
def check_status(self):
439455
if not self.ton.using_alert_bot():
440456
return
@@ -453,6 +469,7 @@ def check_status(self):
453469
self.local.try_function(self.check_stake_sent)
454470
self.local.try_function(self.check_stake_returned)
455471
self.local.try_function(self.check_voting)
472+
self.local.try_function(self.check_initial_sync)
456473

457474
def add_console_commands(self, console):
458475
console.AddItem("enable_alert", self.enable_alert, self.local.translate("enable_alert_cmd"))

modules/prometheus.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ def to_format(self, value):
2626
'stake': Metric('validator_stake', 'Validator stake', 'gauge'),
2727
'celldb_gc_block': Metric('validator_celldb_gc_block', 'Celldb GC block latency', 'gauge'),
2828
'celldb_gc_state': Metric('validator_celldb_gc_state', 'Celldb GC queue size', 'gauge'),
29+
'collated_master_ok': Metric('validator_blocks_collated_master_ok', 'Number of masterchain blocks successfully collated', 'gauge'),
30+
'collated_master_err': Metric('validator_blocks_collated_master_err', 'Number of masterchain blocks failed to collate', 'gauge'),
31+
'collated_shard_ok': Metric('validator_blocks_collated_shard_ok', 'Number of shardchain blocks successfully collated', 'gauge'),
32+
'collated_shard_err': Metric('validator_blocks_collated_shard_err', 'Number of shardchain blocks failed to collate', 'gauge'),
33+
'validated_master_ok': Metric('validator_blocks_validated_master_ok', 'Number of masterchain blocks successfully validated', 'gauge'),
34+
'validated_master_err': Metric('validator_blocks_validated_master_err', 'Number of masterchain blocks failed to validate', 'gauge'),
35+
'validated_shard_ok': Metric('validator_blocks_validated_shard_ok', 'Number of shardchain blocks successfully validated', 'gauge'),
36+
'validated_shard_err': Metric('validator_blocks_validated_shard_err', 'Number of shardchain blocks failed to validate', 'gauge'),
37+
'validator_groups_master': Metric('validator_active_groups_master', 'Number of masterchain validation groups validator participates in', 'gauge'),
38+
'validator_groups_shard': Metric('validator_active_groups_shard', 'Number of shardchain validation groups validator participates in', 'gauge'),
39+
'ls_queries_ok': Metric('validator_ls_queries_ok', 'Number of Liteserver successful queries', 'gauge'),
40+
'ls_queries_err': Metric('validator_ls_queries_err', 'Number of Liteserver failed queries', 'gauge'),
2941
}
3042

3143

@@ -44,7 +56,7 @@ def get_validator_status_metrics(self, result: list):
4456
result.append(METRICS['master_out_of_sync'].to_format(status.masterchain_out_of_sync))
4557
if status.shardchain_out_of_sync is not None:
4658
result.append(METRICS['shard_out_of_sync'].to_format(status.shardchain_out_of_sync))
47-
if status.masterchain_out_of_ser is not None and status.stateserializermasterchainseqno != 0:
59+
if status.stateserializerenabled and status.masterchain_out_of_ser is not None and status.stateserializermasterchainseqno != 0:
4860
result.append(METRICS['out_of_ser'].to_format(status.masterchain_out_of_ser))
4961
if status.masterchainblock is not None and status.gcmasterchainblock is not None:
5062
result.append(METRICS['celldb_gc_block'].to_format(status.masterchainblock - status.gcmasterchainblock))
@@ -53,6 +65,9 @@ def get_validator_status_metrics(self, result: list):
5365
result.append(METRICS['celldb_gc_state'].to_format(status.gcmasterchainblock - status.last_deleted_mc_state))
5466
else:
5567
result.append(METRICS['celldb_gc_state'].to_format(-1))
68+
if status.validator_groups_master is not None:
69+
result.append(METRICS['validator_groups_master'].to_format(status.validator_groups_master))
70+
result.append(METRICS['validator_groups_shard'].to_format(status.validator_groups_shard))
5671
result.append(METRICS['vc_up'].to_format(int(is_working)))
5772

5873
def get_validator_validation_metrics(self, result: list):
@@ -67,6 +82,25 @@ def get_validator_validation_metrics(self, result: list):
6782
if stake:
6883
result.append(METRICS['stake'].to_format(round(stake, 2)))
6984

85+
def get_node_stats_metrics(self, result: list):
86+
stats = self.ton.get_node_statistics()
87+
if stats and 'ls_queries' in stats:
88+
if stats['ls_queries']['time'] < 50:
89+
self.local.add_log(f'Liteserver queries time is too low: {stats}')
90+
return
91+
result.append(METRICS['ls_queries_ok'].to_format(stats['ls_queries']['ok']))
92+
result.append(METRICS['ls_queries_err'].to_format(stats['ls_queries']['error']))
93+
if stats and 'collated' in stats:
94+
result.append(METRICS['collated_master_ok'].to_format(stats['collated']['master']['ok']))
95+
result.append(METRICS['collated_master_err'].to_format(stats['collated']['master']['error']))
96+
result.append(METRICS['collated_shard_ok'].to_format(stats['collated']['shard']['ok']))
97+
result.append(METRICS['collated_shard_err'].to_format(stats['collated']['shard']['error']))
98+
if stats and 'validated' in stats:
99+
result.append(METRICS['validated_master_ok'].to_format(stats['validated']['master']['ok']))
100+
result.append(METRICS['validated_master_err'].to_format(stats['validated']['master']['error']))
101+
result.append(METRICS['validated_shard_ok'].to_format(stats['validated']['shard']['ok']))
102+
result.append(METRICS['validated_shard_err'].to_format(stats['validated']['shard']['error']))
103+
70104
def push_metrics(self):
71105
if not self.ton.using_prometheus():
72106
return
@@ -77,6 +111,7 @@ def push_metrics(self):
77111
metrics = []
78112
self.local.try_function(self.get_validator_status_metrics, args=[metrics])
79113
self.local.try_function(self.get_validator_validation_metrics, args=[metrics])
114+
self.local.try_function(self.get_node_stats_metrics, args=[metrics])
80115
requests.post(url, data='\n'.join(metrics).encode())
81116

82117
def add_console_commands(self, console):

mytoncore/fift.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def Run(self, args, **kwargs):
1919
process = subprocess.run(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout)
2020
output = process.stdout.decode("utf-8")
2121
err = process.stderr.decode("utf-8")
22-
if len(err) > 0:
22+
if process.returncode != 0 and len(err) > 0:
2323
self.local.add_log("args: {args}".format(args=args), "error")
2424
raise Exception("Fift error: {err}".format(err=err))
2525
return output

mytoncore/functions.py

Lines changed: 106 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ def Event(local, event_name):
5353
EnableVcEvent(local)
5454
elif event_name == "validator down":
5555
ValidatorDownEvent(local)
56-
elif event_name == "enable_ton_storage_provider":
57-
enable_ton_storage_provider_event(local)
5856
elif event_name.startswith("enable_mode"):
5957
enable_mode(local, event_name)
6058
elif event_name == "enable_btc_teleport":
@@ -86,15 +84,6 @@ def ValidatorDownEvent(local):
8684
# end define
8785

8886

89-
def enable_ton_storage_provider_event(local):
90-
config_path = local.db.ton_storage.provider.config_path
91-
config = GetConfig(path=config_path)
92-
key_bytes = base64.b64decode(config.ProviderKey)
93-
ton = MyTonCore(local)
94-
ton.import_wallet_with_version(key_bytes[:32], version="v3r2", wallet_name="provider_wallet_001")
95-
#end define
96-
97-
9887
def enable_mode(local, event_name):
9988
ton = MyTonCore(local)
10089
mode = event_name.split("_")[-1]
@@ -292,6 +281,64 @@ def CalculateNetworkStatistics(zerodata, data):
292281
# end define
293282

294283

284+
def save_node_statistics(local, ton):
285+
status = ton.GetValidatorStatus(no_cache=True)
286+
if status.unixtime is None:
287+
return
288+
data = {'timestamp': status.unixtime}
289+
290+
def get_ok_error(value: str):
291+
ok, error = value.split()
292+
return int(ok.split(':')[1]), int(error.split(':')[1])
293+
294+
if 'total.collated_blocks.master' in status:
295+
master_ok, master_error = get_ok_error(status['total.collated_blocks.master'])
296+
shard_ok, shard_error = get_ok_error(status['total.collated_blocks.shard'])
297+
data['collated_blocks'] = {
298+
'master': {'ok': master_ok, 'error': master_error},
299+
'shard': {'ok': shard_ok, 'error': shard_error},
300+
}
301+
if 'total.validated_blocks.master' in status:
302+
master_ok, master_error = get_ok_error(status['total.validated_blocks.master'])
303+
shard_ok, shard_error = get_ok_error(status['total.validated_blocks.shard'])
304+
data['validated_blocks'] = {
305+
'master': {'ok': master_ok, 'error': master_error},
306+
'shard': {'ok': shard_ok, 'error': shard_error},
307+
}
308+
if 'total.ext_msg_check' in status:
309+
ok, error = get_ok_error(status['total.ext_msg_check'])
310+
data['ext_msg_check'] = {'ok': ok, 'error': error}
311+
if 'total.ls_queries_ok' in status and 'total.ls_queries_error' in status:
312+
data['ls_queries'] = {}
313+
for k in status['total.ls_queries_ok'].split():
314+
if k.startswith('TOTAL'):
315+
data['ls_queries']['ok'] = int(k.split(':')[1])
316+
for k in status['total.ls_queries_error'].split():
317+
if k.startswith('TOTAL'):
318+
data['ls_queries']['error'] = int(k.split(':')[1])
319+
statistics = local.db.get("statistics", dict())
320+
321+
if time.time() - int(status.start_time) <= 60: # was node restart <60 sec ago, resetting node statistics
322+
statistics['node'] = []
323+
324+
# statistics['node'] = [stats_from_election_id, stats_from_prev_min, stats_now]
325+
326+
election_id = ton.GetConfig34()['startWorkTime']
327+
if 'node' not in statistics or len(statistics['node']) == 0:
328+
statistics['node'] = [None, data]
329+
elif len(statistics['node']) < 3:
330+
statistics['node'].append(data)
331+
if len(statistics['node']) == 3:
332+
if statistics['node'][0] is None:
333+
if 0 < data['timestamp'] - election_id < 90:
334+
statistics['node'][0] = data
335+
elif statistics['node'][0]['timestamp'] < election_id:
336+
statistics['node'][0] = data
337+
statistics['node'] = statistics.get('node', []) + [data]
338+
statistics['node'].pop(1)
339+
local.db["statistics"] = statistics
340+
341+
295342
def ReadTransData(local, scanner):
296343
transData = local.buffer.transData
297344
SetToTimeData(transData, scanner.transNum)
@@ -417,6 +464,7 @@ def Telemetry(local, ton):
417464
data["vprocess"] = GetValidatorProcessInfo()
418465
data["dbStats"] = local.try_function(get_db_stats)
419466
data["nodeArgs"] = local.try_function(get_node_args)
467+
data["modes"] = local.try_function(ton.get_modes)
420468
data["cpuInfo"] = {'cpuName': local.try_function(get_cpu_name), 'virtual': local.try_function(is_host_virtual)}
421469
data["validatorDiskName"] = local.try_function(get_validator_disk_name)
422470
data["pings"] = local.try_function(get_pings_values)
@@ -550,6 +598,46 @@ def ScanLiteServers(local, ton):
550598
# end define
551599

552600

601+
def check_initial_sync(local, ton):
602+
if not ton.in_initial_sync():
603+
return
604+
validator_status = ton.GetValidatorStatus()
605+
if validator_status.initial_sync:
606+
return
607+
if validator_status.out_of_sync < 20:
608+
ton.set_initial_sync_off()
609+
return
610+
611+
612+
def gc_import(local, ton):
613+
if not ton.local.db.get('importGc', False):
614+
return
615+
local.add_log("GC import is running", "debug")
616+
import_path = '/var/ton-work/db/import'
617+
files = os.listdir(import_path)
618+
if not files:
619+
local.add_log("No files left to import", "debug")
620+
ton.local.db['importGc'] = False
621+
return
622+
try:
623+
status = ton.GetValidatorStatus()
624+
node_seqno = int(status.shardclientmasterchainseqno)
625+
except Exception as e:
626+
local.add_log(f"Failed to get shardclientmasterchainseqno: {e}", "warning")
627+
return
628+
removed = 0
629+
for file in files:
630+
file_seqno = int(file.split('.')[1])
631+
if node_seqno > file_seqno + 101:
632+
try:
633+
os.remove(os.path.join(import_path, file))
634+
removed += 1
635+
except PermissionError:
636+
local.add_log(f"Failed to remove file {file}: Permission denied", "error")
637+
continue
638+
local.add_log(f"Removed {removed} import files up to {node_seqno} seqno", "debug")
639+
640+
553641
def General(local):
554642
local.add_log("start General function", "debug")
555643
ton = MyTonCore(local)
@@ -576,6 +664,8 @@ def General(local):
576664

577665
local.start_cycle(ScanLiteServers, sec=60, args=(local, ton,))
578666

667+
local.start_cycle(save_node_statistics, sec=60, args=(local, ton, ))
668+
579669
from modules.custom_overlays import CustomOverlayModule
580670
local.start_cycle(CustomOverlayModule(ton, local).custom_overlays, sec=60, args=())
581671

@@ -588,6 +678,11 @@ def General(local):
588678
from modules.btc_teleport import BtcTeleportModule
589679
local.start_cycle(BtcTeleportModule(ton, local).auto_vote_offers, sec=600, args=())
590680

681+
if ton.in_initial_sync():
682+
local.start_cycle(check_initial_sync, sec=120, args=(local, ton))
683+
684+
if ton.local.db.get('importGc'):
685+
local.start_cycle(gc_import, sec=300, args=(local, ton))
591686

592687
thr_sleep()
593688
# end define

mytoncore/liteclient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def Run(self, cmd, **kwargs):
2222
if index is not None:
2323
index = str(index)
2424
args += ["-i", index]
25-
elif useLocalLiteServer and self.pubkeyPath and validator_status.out_of_sync and validator_status.out_of_sync < 20:
25+
elif useLocalLiteServer and self.pubkeyPath and validator_status.out_of_sync is not None and validator_status.out_of_sync < 20:
2626
args = [self.appPath, "--addr", self.addr, "--pub", self.pubkeyPath, "--verbosity", "0", "--cmd", cmd]
2727
else:
2828
liteServers = self.local.db.get("liteServers")

0 commit comments

Comments
 (0)